c5429db379d7b19c987cd5faf5ba3070ac0a1ffd
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13 /**
14  * @class Roo.data.SortTypes
15  * @static
16  * Defines the default sorting (casting?) comparison functions used when sorting data.
17  */
18 Roo.data.SortTypes = {
19     /**
20      * Default sort that does nothing
21      * @param {Mixed} s The value being converted
22      * @return {Mixed} The comparison value
23      */
24     none : function(s){
25         return s;
26     },
27     
28     /**
29      * The regular expression used to strip tags
30      * @type {RegExp}
31      * @property
32      */
33     stripTagsRE : /<\/?[^>]+>/gi,
34     
35     /**
36      * Strips all HTML tags to sort on text only
37      * @param {Mixed} s The value being converted
38      * @return {String} The comparison value
39      */
40     asText : function(s){
41         return String(s).replace(this.stripTagsRE, "");
42     },
43     
44     /**
45      * Strips all HTML tags to sort on text only - Case insensitive
46      * @param {Mixed} s The value being converted
47      * @return {String} The comparison value
48      */
49     asUCText : function(s){
50         return String(s).toUpperCase().replace(this.stripTagsRE, "");
51     },
52     
53     /**
54      * Case insensitive string
55      * @param {Mixed} s The value being converted
56      * @return {String} The comparison value
57      */
58     asUCString : function(s) {
59         return String(s).toUpperCase();
60     },
61     
62     /**
63      * Date sorting
64      * @param {Mixed} s The value being converted
65      * @return {Number} The comparison value
66      */
67     asDate : function(s) {
68         if(!s){
69             return 0;
70         }
71         if(s instanceof Date){
72             return s.getTime();
73         }
74         return Date.parse(String(s));
75     },
76     
77     /**
78      * Float sorting
79      * @param {Mixed} s The value being converted
80      * @return {Float} The comparison value
81      */
82     asFloat : function(s) {
83         var val = parseFloat(String(s).replace(/,/g, ""));
84         if(isNaN(val)) {
85             val = 0;
86         }
87         return val;
88     },
89     
90     /**
91      * Integer sorting
92      * @param {Mixed} s The value being converted
93      * @return {Number} The comparison value
94      */
95     asInt : function(s) {
96         var val = parseInt(String(s).replace(/,/g, ""));
97         if(isNaN(val)) {
98             val = 0;
99         }
100         return val;
101     }
102 };/*
103  * Based on:
104  * Ext JS Library 1.1.1
105  * Copyright(c) 2006-2007, Ext JS, LLC.
106  *
107  * Originally Released Under LGPL - original licence link has changed is not relivant.
108  *
109  * Fork - LGPL
110  * <script type="text/javascript">
111  */
112
113 /**
114 * @class Roo.data.Record
115  * Instances of this class encapsulate both record <em>definition</em> information, and record
116  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
117  * to access Records cached in an {@link Roo.data.Store} object.<br>
118  * <p>
119  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
120  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
121  * objects.<br>
122  * <p>
123  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
124  * @constructor
125  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
126  * {@link #create}. The parameters are the same.
127  * @param {Array} data An associative Array of data values keyed by the field name.
128  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
129  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
130  * not specified an integer id is generated.
131  */
132 Roo.data.Record = function(data, id){
133     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
134     this.data = data;
135 };
136
137 /**
138  * Generate a constructor for a specific record layout.
139  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
140  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
141  * Each field definition object may contain the following properties: <ul>
142  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
143  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
144  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
145  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
146  * is being used, then this is a string containing the javascript expression to reference the data relative to 
147  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
148  * to the data item relative to the record element. If the mapping expression is the same as the field name,
149  * this may be omitted.</p></li>
150  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
151  * <ul><li>auto (Default, implies no conversion)</li>
152  * <li>string</li>
153  * <li>int</li>
154  * <li>float</li>
155  * <li>boolean</li>
156  * <li>date</li></ul></p></li>
157  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
158  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
159  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
160  * by the Reader into an object that will be stored in the Record. It is passed the
161  * following parameters:<ul>
162  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
163  * </ul></p></li>
164  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
165  * </ul>
166  * <br>usage:<br><pre><code>
167 var TopicRecord = Roo.data.Record.create(
168     {name: 'title', mapping: 'topic_title'},
169     {name: 'author', mapping: 'username'},
170     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
171     {name: 'lastPost', mapping: 'post_time', type: 'date'},
172     {name: 'lastPoster', mapping: 'user2'},
173     {name: 'excerpt', mapping: 'post_text'}
174 );
175
176 var myNewRecord = new TopicRecord({
177     title: 'Do my job please',
178     author: 'noobie',
179     totalPosts: 1,
180     lastPost: new Date(),
181     lastPoster: 'Animal',
182     excerpt: 'No way dude!'
183 });
184 myStore.add(myNewRecord);
185 </code></pre>
186  * @method create
187  * @static
188  */
189 Roo.data.Record.create = function(o){
190     var f = function(){
191         f.superclass.constructor.apply(this, arguments);
192     };
193     Roo.extend(f, Roo.data.Record);
194     var p = f.prototype;
195     p.fields = new Roo.util.MixedCollection(false, function(field){
196         return field.name;
197     });
198     for(var i = 0, len = o.length; i < len; i++){
199         p.fields.add(new Roo.data.Field(o[i]));
200     }
201     f.getField = function(name){
202         return p.fields.get(name);  
203     };
204     return f;
205 };
206
207 Roo.data.Record.AUTO_ID = 1000;
208 Roo.data.Record.EDIT = 'edit';
209 Roo.data.Record.REJECT = 'reject';
210 Roo.data.Record.COMMIT = 'commit';
211
212 Roo.data.Record.prototype = {
213     /**
214      * Readonly flag - true if this record has been modified.
215      * @type Boolean
216      */
217     dirty : false,
218     editing : false,
219     error: null,
220     modified: null,
221
222     // private
223     join : function(store){
224         this.store = store;
225     },
226
227     /**
228      * Set the named field to the specified value.
229      * @param {String} name The name of the field to set.
230      * @param {Object} value The value to set the field to.
231      */
232     set : function(name, value){
233         if(this.data[name] == value){
234             return;
235         }
236         this.dirty = true;
237         if(!this.modified){
238             this.modified = {};
239         }
240         if(typeof this.modified[name] == 'undefined'){
241             this.modified[name] = this.data[name];
242         }
243         this.data[name] = value;
244         if(!this.editing && this.store){
245             this.store.afterEdit(this);
246         }       
247     },
248
249     /**
250      * Get the value of the named field.
251      * @param {String} name The name of the field to get the value of.
252      * @return {Object} The value of the field.
253      */
254     get : function(name){
255         return this.data[name]; 
256     },
257
258     // private
259     beginEdit : function(){
260         this.editing = true;
261         this.modified = {}; 
262     },
263
264     // private
265     cancelEdit : function(){
266         this.editing = false;
267         delete this.modified;
268     },
269
270     // private
271     endEdit : function(){
272         this.editing = false;
273         if(this.dirty && this.store){
274             this.store.afterEdit(this);
275         }
276     },
277
278     /**
279      * Usually called by the {@link Roo.data.Store} which owns the Record.
280      * Rejects all changes made to the Record since either creation, or the last commit operation.
281      * Modified fields are reverted to their original values.
282      * <p>
283      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
284      * of reject operations.
285      */
286     reject : function(){
287         var m = this.modified;
288         for(var n in m){
289             if(typeof m[n] != "function"){
290                 this.data[n] = m[n];
291             }
292         }
293         this.dirty = false;
294         delete this.modified;
295         this.editing = false;
296         if(this.store){
297             this.store.afterReject(this);
298         }
299     },
300
301     /**
302      * Usually called by the {@link Roo.data.Store} which owns the Record.
303      * Commits all changes made to the Record since either creation, or the last commit operation.
304      * <p>
305      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
306      * of commit operations.
307      */
308     commit : function(){
309         this.dirty = false;
310         delete this.modified;
311         this.editing = false;
312         if(this.store){
313             this.store.afterCommit(this);
314         }
315     },
316
317     // private
318     hasError : function(){
319         return this.error != null;
320     },
321
322     // private
323     clearError : function(){
324         this.error = null;
325     },
326
327     /**
328      * Creates a copy of this record.
329      * @param {String} id (optional) A new record id if you don't want to use this record's id
330      * @return {Record}
331      */
332     copy : function(newId) {
333         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
334     }
335 };/*
336  * Based on:
337  * Ext JS Library 1.1.1
338  * Copyright(c) 2006-2007, Ext JS, LLC.
339  *
340  * Originally Released Under LGPL - original licence link has changed is not relivant.
341  *
342  * Fork - LGPL
343  * <script type="text/javascript">
344  */
345
346
347
348 /**
349  * @class Roo.data.Store
350  * @extends Roo.util.Observable
351  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
352  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
353  * <p>
354  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
355  * has no knowledge of the format of the data returned by the Proxy.<br>
356  * <p>
357  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
358  * instances from the data object. These records are cached and made available through accessor functions.
359  * @constructor
360  * Creates a new Store.
361  * @param {Object} config A config object containing the objects needed for the Store to access data,
362  * and read the data into Records.
363  */
364 Roo.data.Store = function(config){
365     this.data = new Roo.util.MixedCollection(false);
366     this.data.getKey = function(o){
367         return o.id;
368     };
369     this.baseParams = {};
370     // private
371     this.paramNames = {
372         "start" : "start",
373         "limit" : "limit",
374         "sort" : "sort",
375         "dir" : "dir",
376         "multisort" : "_multisort"
377     };
378
379     if(config && config.data){
380         this.inlineData = config.data;
381         delete config.data;
382     }
383
384     Roo.apply(this, config);
385     
386     if(this.reader){ // reader passed
387         this.reader = Roo.factory(this.reader, Roo.data);
388         this.reader.xmodule = this.xmodule || false;
389         if(!this.recordType){
390             this.recordType = this.reader.recordType;
391         }
392         if(this.reader.onMetaChange){
393             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
394         }
395     }
396
397     if(this.recordType){
398         this.fields = this.recordType.prototype.fields;
399     }
400     this.modified = [];
401
402     this.addEvents({
403         /**
404          * @event datachanged
405          * Fires when the data cache has changed, and a widget which is using this Store
406          * as a Record cache should refresh its view.
407          * @param {Store} this
408          */
409         datachanged : true,
410         /**
411          * @event metachange
412          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
413          * @param {Store} this
414          * @param {Object} meta The JSON metadata
415          */
416         metachange : true,
417         /**
418          * @event add
419          * Fires when Records have been added to the Store
420          * @param {Store} this
421          * @param {Roo.data.Record[]} records The array of Records added
422          * @param {Number} index The index at which the record(s) were added
423          */
424         add : true,
425         /**
426          * @event remove
427          * Fires when a Record has been removed from the Store
428          * @param {Store} this
429          * @param {Roo.data.Record} record The Record that was removed
430          * @param {Number} index The index at which the record was removed
431          */
432         remove : true,
433         /**
434          * @event update
435          * Fires when a Record has been updated
436          * @param {Store} this
437          * @param {Roo.data.Record} record The Record that was updated
438          * @param {String} operation The update operation being performed.  Value may be one of:
439          * <pre><code>
440  Roo.data.Record.EDIT
441  Roo.data.Record.REJECT
442  Roo.data.Record.COMMIT
443          * </code></pre>
444          */
445         update : true,
446         /**
447          * @event clear
448          * Fires when the data cache has been cleared.
449          * @param {Store} this
450          */
451         clear : true,
452         /**
453          * @event beforeload
454          * Fires before a request is made for a new data object.  If the beforeload handler returns false
455          * the load action will be canceled.
456          * @param {Store} this
457          * @param {Object} options The loading options that were specified (see {@link #load} for details)
458          */
459         beforeload : true,
460         /**
461          * @event beforeloadadd
462          * Fires after a new set of Records has been loaded.
463          * @param {Store} this
464          * @param {Roo.data.Record[]} records The Records that were loaded
465          * @param {Object} options The loading options that were specified (see {@link #load} for details)
466          */
467         beforeloadadd : true,
468         /**
469          * @event load
470          * Fires after a new set of Records has been loaded, before they are added to the store.
471          * @param {Store} this
472          * @param {Roo.data.Record[]} records The Records that were loaded
473          * @param {Object} options The loading options that were specified (see {@link #load} for details)
474          * @params {Object} return from reader
475          */
476         load : true,
477         /**
478          * @event loadexception
479          * Fires if an exception occurs in the Proxy during loading.
480          * Called with the signature of the Proxy's "loadexception" event.
481          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
482          * 
483          * @param {Proxy} 
484          * @param {Object} return from JsonData.reader() - success, totalRecords, records
485          * @param {Object} load options 
486          * @param {Object} jsonData from your request (normally this contains the Exception)
487          */
488         loadexception : true
489     });
490     
491     if(this.proxy){
492         this.proxy = Roo.factory(this.proxy, Roo.data);
493         this.proxy.xmodule = this.xmodule || false;
494         this.relayEvents(this.proxy,  ["loadexception"]);
495     }
496     this.sortToggle = {};
497     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
498
499     Roo.data.Store.superclass.constructor.call(this);
500
501     if(this.inlineData){
502         this.loadData(this.inlineData);
503         delete this.inlineData;
504     }
505 };
506
507 Roo.extend(Roo.data.Store, Roo.util.Observable, {
508      /**
509     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
510     * without a remote query - used by combo/forms at present.
511     */
512     
513     /**
514     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
515     */
516     /**
517     * @cfg {Array} data Inline data to be loaded when the store is initialized.
518     */
519     /**
520     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
521     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
522     */
523     /**
524     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
525     * on any HTTP request
526     */
527     /**
528     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
529     */
530     /**
531     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
532     */
533     multiSort: false,
534     /**
535     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
536     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
537     */
538     remoteSort : false,
539
540     /**
541     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
542      * loaded or when a record is removed. (defaults to false).
543     */
544     pruneModifiedRecords : false,
545
546     // private
547     lastOptions : null,
548
549     /**
550      * Add Records to the Store and fires the add event.
551      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
552      */
553     add : function(records){
554         records = [].concat(records);
555         for(var i = 0, len = records.length; i < len; i++){
556             records[i].join(this);
557         }
558         var index = this.data.length;
559         this.data.addAll(records);
560         this.fireEvent("add", this, records, index);
561     },
562
563     /**
564      * Remove a Record from the Store and fires the remove event.
565      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
566      */
567     remove : function(record){
568         var index = this.data.indexOf(record);
569         this.data.removeAt(index);
570  
571         if(this.pruneModifiedRecords){
572             this.modified.remove(record);
573         }
574         this.fireEvent("remove", this, record, index);
575     },
576
577     /**
578      * Remove all Records from the Store and fires the clear event.
579      */
580     removeAll : function(){
581         this.data.clear();
582         if(this.pruneModifiedRecords){
583             this.modified = [];
584         }
585         this.fireEvent("clear", this);
586     },
587
588     /**
589      * Inserts Records to the Store at the given index and fires the add event.
590      * @param {Number} index The start index at which to insert the passed Records.
591      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
592      */
593     insert : function(index, records){
594         records = [].concat(records);
595         for(var i = 0, len = records.length; i < len; i++){
596             this.data.insert(index, records[i]);
597             records[i].join(this);
598         }
599         this.fireEvent("add", this, records, index);
600     },
601
602     /**
603      * Get the index within the cache of the passed Record.
604      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
605      * @return {Number} The index of the passed Record. Returns -1 if not found.
606      */
607     indexOf : function(record){
608         return this.data.indexOf(record);
609     },
610
611     /**
612      * Get the index within the cache of the Record with the passed id.
613      * @param {String} id The id of the Record to find.
614      * @return {Number} The index of the Record. Returns -1 if not found.
615      */
616     indexOfId : function(id){
617         return this.data.indexOfKey(id);
618     },
619
620     /**
621      * Get the Record with the specified id.
622      * @param {String} id The id of the Record to find.
623      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
624      */
625     getById : function(id){
626         return this.data.key(id);
627     },
628
629     /**
630      * Get the Record at the specified index.
631      * @param {Number} index The index of the Record to find.
632      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
633      */
634     getAt : function(index){
635         return this.data.itemAt(index);
636     },
637
638     /**
639      * Returns a range of Records between specified indices.
640      * @param {Number} startIndex (optional) The starting index (defaults to 0)
641      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
642      * @return {Roo.data.Record[]} An array of Records
643      */
644     getRange : function(start, end){
645         return this.data.getRange(start, end);
646     },
647
648     // private
649     storeOptions : function(o){
650         o = Roo.apply({}, o);
651         delete o.callback;
652         delete o.scope;
653         this.lastOptions = o;
654     },
655
656     /**
657      * Loads the Record cache from the configured Proxy using the configured Reader.
658      * <p>
659      * If using remote paging, then the first load call must specify the <em>start</em>
660      * and <em>limit</em> properties in the options.params property to establish the initial
661      * position within the dataset, and the number of Records to cache on each read from the Proxy.
662      * <p>
663      * <strong>It is important to note that for remote data sources, loading is asynchronous,
664      * and this call will return before the new data has been loaded. Perform any post-processing
665      * in a callback function, or in a "load" event handler.</strong>
666      * <p>
667      * @param {Object} options An object containing properties which control loading options:<ul>
668      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
669      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
670      * passed the following arguments:<ul>
671      * <li>r : Roo.data.Record[]</li>
672      * <li>options: Options object from the load call</li>
673      * <li>success: Boolean success indicator</li></ul></li>
674      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
675      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
676      * </ul>
677      */
678     load : function(options){
679         options = options || {};
680         if(this.fireEvent("beforeload", this, options) !== false){
681             this.storeOptions(options);
682             var p = Roo.apply(options.params || {}, this.baseParams);
683             // if meta was not loaded from remote source.. try requesting it.
684             if (!this.reader.metaFromRemote) {
685                 p._requestMeta = 1;
686             }
687             if(this.sortInfo && this.remoteSort){
688                 var pn = this.paramNames;
689                 p[pn["sort"]] = this.sortInfo.field;
690                 p[pn["dir"]] = this.sortInfo.direction;
691             }
692             if (this.multiSort) {
693                 var pn = this.paramNames;
694                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
695             }
696             
697             this.proxy.load(p, this.reader, this.loadRecords, this, options);
698         }
699     },
700
701     /**
702      * Reloads the Record cache from the configured Proxy using the configured Reader and
703      * the options from the last load operation performed.
704      * @param {Object} options (optional) An object containing properties which may override the options
705      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
706      * the most recently used options are reused).
707      */
708     reload : function(options){
709         this.load(Roo.applyIf(options||{}, this.lastOptions));
710     },
711
712     // private
713     // Called as a callback by the Reader during a load operation.
714     loadRecords : function(o, options, success){
715          
716         if(!o){
717             if(success !== false){
718                 this.fireEvent("load", this, [], options, o);
719             }
720             if(options.callback){
721                 options.callback.call(options.scope || this, [], options, false);
722             }
723             return;
724         }
725         // if data returned failure - throw an exception.
726         if (o.success === false) {
727             // show a message if no listener is registered.
728             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
729                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
730             }
731             // loadmask wil be hooked into this..
732             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
733             return;
734         }
735         var r = o.records, t = o.totalRecords || r.length;
736         
737         this.fireEvent("beforeloadadd", this, r, options, o);
738         
739         if(!options || options.add !== true){
740             if(this.pruneModifiedRecords){
741                 this.modified = [];
742             }
743             for(var i = 0, len = r.length; i < len; i++){
744                 r[i].join(this);
745             }
746             if(this.snapshot){
747                 this.data = this.snapshot;
748                 delete this.snapshot;
749             }
750             this.data.clear();
751             this.data.addAll(r);
752             this.totalLength = t;
753             this.applySort();
754             this.fireEvent("datachanged", this);
755         }else{
756             this.totalLength = Math.max(t, this.data.length+r.length);
757             this.add(r);
758         }
759         
760         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
761                 
762             var e = new Roo.data.Record({});
763
764             e.set(this.parent.displayField, this.parent.emptyTitle);
765             e.set(this.parent.valueField, '');
766
767             this.insert(0, e);
768         }
769             
770         this.fireEvent("load", this, r, options, o);
771         if(options.callback){
772             options.callback.call(options.scope || this, r, options, true);
773         }
774     },
775
776
777     /**
778      * Loads data from a passed data block. A Reader which understands the format of the data
779      * must have been configured in the constructor.
780      * @param {Object} data The data block from which to read the Records.  The format of the data expected
781      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
782      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
783      */
784     loadData : function(o, append){
785         var r = this.reader.readRecords(o);
786         this.loadRecords(r, {add: append}, true);
787     },
788     
789      /**
790      * using 'cn' the nested child reader read the child array into it's child stores.
791      * @param {Object} rec The record with a 'children array
792      */
793     loadDataFromChildren : function(rec)
794     {
795         this.loadData(this.reader.toLoadData(rec));
796     },
797     
798
799     /**
800      * Gets the number of cached records.
801      * <p>
802      * <em>If using paging, this may not be the total size of the dataset. If the data object
803      * used by the Reader contains the dataset size, then the getTotalCount() function returns
804      * the data set size</em>
805      */
806     getCount : function(){
807         return this.data.length || 0;
808     },
809
810     /**
811      * Gets the total number of records in the dataset as returned by the server.
812      * <p>
813      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
814      * the dataset size</em>
815      */
816     getTotalCount : function(){
817         return this.totalLength || 0;
818     },
819
820     /**
821      * Returns the sort state of the Store as an object with two properties:
822      * <pre><code>
823  field {String} The name of the field by which the Records are sorted
824  direction {String} The sort order, "ASC" or "DESC"
825      * </code></pre>
826      */
827     getSortState : function(){
828         return this.sortInfo;
829     },
830
831     // private
832     applySort : function(){
833         if(this.sortInfo && !this.remoteSort){
834             var s = this.sortInfo, f = s.field;
835             var st = this.fields.get(f).sortType;
836             var fn = function(r1, r2){
837                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
838                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
839             };
840             this.data.sort(s.direction, fn);
841             if(this.snapshot && this.snapshot != this.data){
842                 this.snapshot.sort(s.direction, fn);
843             }
844         }
845     },
846
847     /**
848      * Sets the default sort column and order to be used by the next load operation.
849      * @param {String} fieldName The name of the field to sort by.
850      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
851      */
852     setDefaultSort : function(field, dir){
853         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
854     },
855
856     /**
857      * Sort the Records.
858      * If remote sorting is used, the sort is performed on the server, and the cache is
859      * reloaded. If local sorting is used, the cache is sorted internally.
860      * @param {String} fieldName The name of the field to sort by.
861      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
862      */
863     sort : function(fieldName, dir){
864         var f = this.fields.get(fieldName);
865         if(!dir){
866             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
867             
868             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
869                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
870             }else{
871                 dir = f.sortDir;
872             }
873         }
874         this.sortToggle[f.name] = dir;
875         this.sortInfo = {field: f.name, direction: dir};
876         if(!this.remoteSort){
877             this.applySort();
878             this.fireEvent("datachanged", this);
879         }else{
880             this.load(this.lastOptions);
881         }
882     },
883
884     /**
885      * Calls the specified function for each of the Records in the cache.
886      * @param {Function} fn The function to call. The Record is passed as the first parameter.
887      * Returning <em>false</em> aborts and exits the iteration.
888      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
889      */
890     each : function(fn, scope){
891         this.data.each(fn, scope);
892     },
893
894     /**
895      * Gets all records modified since the last commit.  Modified records are persisted across load operations
896      * (e.g., during paging).
897      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
898      */
899     getModifiedRecords : function(){
900         return this.modified;
901     },
902
903     // private
904     createFilterFn : function(property, value, anyMatch){
905         if(!value.exec){ // not a regex
906             value = String(value);
907             if(value.length == 0){
908                 return false;
909             }
910             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
911         }
912         return function(r){
913             return value.test(r.data[property]);
914         };
915     },
916
917     /**
918      * Sums the value of <i>property</i> for each record between start and end and returns the result.
919      * @param {String} property A field on your records
920      * @param {Number} start The record index to start at (defaults to 0)
921      * @param {Number} end The last record index to include (defaults to length - 1)
922      * @return {Number} The sum
923      */
924     sum : function(property, start, end){
925         var rs = this.data.items, v = 0;
926         start = start || 0;
927         end = (end || end === 0) ? end : rs.length-1;
928
929         for(var i = start; i <= end; i++){
930             v += (rs[i].data[property] || 0);
931         }
932         return v;
933     },
934
935     /**
936      * Filter the records by a specified property.
937      * @param {String} field A field on your records
938      * @param {String/RegExp} value Either a string that the field
939      * should start with or a RegExp to test against the field
940      * @param {Boolean} anyMatch True to match any part not just the beginning
941      */
942     filter : function(property, value, anyMatch){
943         var fn = this.createFilterFn(property, value, anyMatch);
944         return fn ? this.filterBy(fn) : this.clearFilter();
945     },
946
947     /**
948      * Filter by a function. The specified function will be called with each
949      * record in this data source. If the function returns true the record is included,
950      * otherwise it is filtered.
951      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
952      * @param {Object} scope (optional) The scope of the function (defaults to this)
953      */
954     filterBy : function(fn, scope){
955         this.snapshot = this.snapshot || this.data;
956         this.data = this.queryBy(fn, scope||this);
957         this.fireEvent("datachanged", this);
958     },
959
960     /**
961      * Query the records by a specified property.
962      * @param {String} field A field on your records
963      * @param {String/RegExp} value Either a string that the field
964      * should start with or a RegExp to test against the field
965      * @param {Boolean} anyMatch True to match any part not just the beginning
966      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
967      */
968     query : function(property, value, anyMatch){
969         var fn = this.createFilterFn(property, value, anyMatch);
970         return fn ? this.queryBy(fn) : this.data.clone();
971     },
972
973     /**
974      * Query by a function. The specified function will be called with each
975      * record in this data source. If the function returns true the record is included
976      * in the results.
977      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
978      * @param {Object} scope (optional) The scope of the function (defaults to this)
979       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
980      **/
981     queryBy : function(fn, scope){
982         var data = this.snapshot || this.data;
983         return data.filterBy(fn, scope||this);
984     },
985
986     /**
987      * Collects unique values for a particular dataIndex from this store.
988      * @param {String} dataIndex The property to collect
989      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
990      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
991      * @return {Array} An array of the unique values
992      **/
993     collect : function(dataIndex, allowNull, bypassFilter){
994         var d = (bypassFilter === true && this.snapshot) ?
995                 this.snapshot.items : this.data.items;
996         var v, sv, r = [], l = {};
997         for(var i = 0, len = d.length; i < len; i++){
998             v = d[i].data[dataIndex];
999             sv = String(v);
1000             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
1001                 l[sv] = true;
1002                 r[r.length] = v;
1003             }
1004         }
1005         return r;
1006     },
1007
1008     /**
1009      * Revert to a view of the Record cache with no filtering applied.
1010      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
1011      */
1012     clearFilter : function(suppressEvent){
1013         if(this.snapshot && this.snapshot != this.data){
1014             this.data = this.snapshot;
1015             delete this.snapshot;
1016             if(suppressEvent !== true){
1017                 this.fireEvent("datachanged", this);
1018             }
1019         }
1020     },
1021
1022     // private
1023     afterEdit : function(record){
1024         if(this.modified.indexOf(record) == -1){
1025             this.modified.push(record);
1026         }
1027         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1028     },
1029     
1030     // private
1031     afterReject : function(record){
1032         this.modified.remove(record);
1033         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1034     },
1035
1036     // private
1037     afterCommit : function(record){
1038         this.modified.remove(record);
1039         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1040     },
1041
1042     /**
1043      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1044      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1045      */
1046     commitChanges : function(){
1047         var m = this.modified.slice(0);
1048         this.modified = [];
1049         for(var i = 0, len = m.length; i < len; i++){
1050             m[i].commit();
1051         }
1052     },
1053
1054     /**
1055      * Cancel outstanding changes on all changed records.
1056      */
1057     rejectChanges : function(){
1058         var m = this.modified.slice(0);
1059         this.modified = [];
1060         for(var i = 0, len = m.length; i < len; i++){
1061             m[i].reject();
1062         }
1063     },
1064
1065     onMetaChange : function(meta, rtype, o){
1066         this.recordType = rtype;
1067         this.fields = rtype.prototype.fields;
1068         delete this.snapshot;
1069         this.sortInfo = meta.sortInfo || this.sortInfo;
1070         this.modified = [];
1071         this.fireEvent('metachange', this, this.reader.meta);
1072     },
1073     
1074     moveIndex : function(data, type)
1075     {
1076         var index = this.indexOf(data);
1077         
1078         var newIndex = index + type;
1079         
1080         this.remove(data);
1081         
1082         this.insert(newIndex, data);
1083         
1084     }
1085 });/*
1086  * Based on:
1087  * Ext JS Library 1.1.1
1088  * Copyright(c) 2006-2007, Ext JS, LLC.
1089  *
1090  * Originally Released Under LGPL - original licence link has changed is not relivant.
1091  *
1092  * Fork - LGPL
1093  * <script type="text/javascript">
1094  */
1095
1096 /**
1097  * @class Roo.data.SimpleStore
1098  * @extends Roo.data.Store
1099  * Small helper class to make creating Stores from Array data easier.
1100  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1101  * @cfg {Array} fields An array of field definition objects, or field name strings.
1102  * @cfg {Object} an existing reader (eg. copied from another store)
1103  * @cfg {Array} data The multi-dimensional array of data
1104  * @cfg {Roo.data.DataProxy} proxy [not-required]  
1105  * @cfg {Roo.data.Reader} reader  [not-required] 
1106  * @constructor
1107  * @param {Object} config
1108  */
1109 Roo.data.SimpleStore = function(config)
1110 {
1111     Roo.data.SimpleStore.superclass.constructor.call(this, {
1112         isLocal : true,
1113         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
1114                 id: config.id
1115             },
1116             Roo.data.Record.create(config.fields)
1117         ),
1118         proxy : new Roo.data.MemoryProxy(config.data)
1119     });
1120     this.load();
1121 };
1122 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1123  * Based on:
1124  * Ext JS Library 1.1.1
1125  * Copyright(c) 2006-2007, Ext JS, LLC.
1126  *
1127  * Originally Released Under LGPL - original licence link has changed is not relivant.
1128  *
1129  * Fork - LGPL
1130  * <script type="text/javascript">
1131  */
1132
1133 /**
1134 /**
1135  * @extends Roo.data.Store
1136  * @class Roo.data.JsonStore
1137  * Small helper class to make creating Stores for JSON data easier. <br/>
1138 <pre><code>
1139 var store = new Roo.data.JsonStore({
1140     url: 'get-images.php',
1141     root: 'images',
1142     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1143 });
1144 </code></pre>
1145  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1146  * JsonReader and HttpProxy (unless inline data is provided).</b>
1147  * @cfg {Array} fields An array of field definition objects, or field name strings.
1148  * @constructor
1149  * @param {Object} config
1150  */
1151 Roo.data.JsonStore = function(c){
1152     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1153         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1154         reader: new Roo.data.JsonReader(c, c.fields)
1155     }));
1156 };
1157 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1158  * Based on:
1159  * Ext JS Library 1.1.1
1160  * Copyright(c) 2006-2007, Ext JS, LLC.
1161  *
1162  * Originally Released Under LGPL - original licence link has changed is not relivant.
1163  *
1164  * Fork - LGPL
1165  * <script type="text/javascript">
1166  */
1167
1168  
1169 Roo.data.Field = function(config){
1170     if(typeof config == "string"){
1171         config = {name: config};
1172     }
1173     Roo.apply(this, config);
1174     
1175     if(!this.type){
1176         this.type = "auto";
1177     }
1178     
1179     var st = Roo.data.SortTypes;
1180     // named sortTypes are supported, here we look them up
1181     if(typeof this.sortType == "string"){
1182         this.sortType = st[this.sortType];
1183     }
1184     
1185     // set default sortType for strings and dates
1186     if(!this.sortType){
1187         switch(this.type){
1188             case "string":
1189                 this.sortType = st.asUCString;
1190                 break;
1191             case "date":
1192                 this.sortType = st.asDate;
1193                 break;
1194             default:
1195                 this.sortType = st.none;
1196         }
1197     }
1198
1199     // define once
1200     var stripRe = /[\$,%]/g;
1201
1202     // prebuilt conversion function for this field, instead of
1203     // switching every time we're reading a value
1204     if(!this.convert){
1205         var cv, dateFormat = this.dateFormat;
1206         switch(this.type){
1207             case "":
1208             case "auto":
1209             case undefined:
1210                 cv = function(v){ return v; };
1211                 break;
1212             case "string":
1213                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1214                 break;
1215             case "int":
1216                 cv = function(v){
1217                     return v !== undefined && v !== null && v !== '' ?
1218                            parseInt(String(v).replace(stripRe, ""), 10) : '';
1219                     };
1220                 break;
1221             case "float":
1222                 cv = function(v){
1223                     return v !== undefined && v !== null && v !== '' ?
1224                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
1225                     };
1226                 break;
1227             case "bool":
1228             case "boolean":
1229                 cv = function(v){ return v === true || v === "true" || v == 1; };
1230                 break;
1231             case "date":
1232                 cv = function(v){
1233                     if(!v){
1234                         return '';
1235                     }
1236                     if(v instanceof Date){
1237                         return v;
1238                     }
1239                     if(dateFormat){
1240                         if(dateFormat == "timestamp"){
1241                             return new Date(v*1000);
1242                         }
1243                         return Date.parseDate(v, dateFormat);
1244                     }
1245                     var parsed = Date.parse(v);
1246                     return parsed ? new Date(parsed) : null;
1247                 };
1248              break;
1249             
1250         }
1251         this.convert = cv;
1252     }
1253 };
1254
1255 Roo.data.Field.prototype = {
1256     dateFormat: null,
1257     defaultValue: "",
1258     mapping: null,
1259     sortType : null,
1260     sortDir : "ASC"
1261 };/*
1262  * Based on:
1263  * Ext JS Library 1.1.1
1264  * Copyright(c) 2006-2007, Ext JS, LLC.
1265  *
1266  * Originally Released Under LGPL - original licence link has changed is not relivant.
1267  *
1268  * Fork - LGPL
1269  * <script type="text/javascript">
1270  */
1271  
1272 // Base class for reading structured data from a data source.  This class is intended to be
1273 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1274
1275 /**
1276  * @class Roo.data.DataReader
1277  * @abstract
1278  * Base class for reading structured data from a data source.  This class is intended to be
1279  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1280  */
1281
1282 Roo.data.DataReader = function(meta, recordType){
1283     
1284     this.meta = meta;
1285     
1286     this.recordType = recordType instanceof Array ? 
1287         Roo.data.Record.create(recordType) : recordType;
1288 };
1289
1290 Roo.data.DataReader.prototype = {
1291     
1292     
1293     readerType : 'Data',
1294      /**
1295      * Create an empty record
1296      * @param {Object} data (optional) - overlay some values
1297      * @return {Roo.data.Record} record created.
1298      */
1299     newRow :  function(d) {
1300         var da =  {};
1301         this.recordType.prototype.fields.each(function(c) {
1302             switch( c.type) {
1303                 case 'int' : da[c.name] = 0; break;
1304                 case 'date' : da[c.name] = new Date(); break;
1305                 case 'float' : da[c.name] = 0.0; break;
1306                 case 'boolean' : da[c.name] = false; break;
1307                 default : da[c.name] = ""; break;
1308             }
1309             
1310         });
1311         return new this.recordType(Roo.apply(da, d));
1312     }
1313     
1314     
1315 };/*
1316  * Based on:
1317  * Ext JS Library 1.1.1
1318  * Copyright(c) 2006-2007, Ext JS, LLC.
1319  *
1320  * Originally Released Under LGPL - original licence link has changed is not relivant.
1321  *
1322  * Fork - LGPL
1323  * <script type="text/javascript">
1324  */
1325
1326 /**
1327  * @class Roo.data.DataProxy
1328  * @extends Roo.util.Observable
1329  * @abstract
1330  * This class is an abstract base class for implementations which provide retrieval of
1331  * unformatted data objects.<br>
1332  * <p>
1333  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1334  * (of the appropriate type which knows how to parse the data object) to provide a block of
1335  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1336  * <p>
1337  * Custom implementations must implement the load method as described in
1338  * {@link Roo.data.HttpProxy#load}.
1339  */
1340 Roo.data.DataProxy = function(){
1341     this.addEvents({
1342         /**
1343          * @event beforeload
1344          * Fires before a network request is made to retrieve a data object.
1345          * @param {Object} This DataProxy object.
1346          * @param {Object} params The params parameter to the load function.
1347          */
1348         beforeload : true,
1349         /**
1350          * @event load
1351          * Fires before the load method's callback is called.
1352          * @param {Object} This DataProxy object.
1353          * @param {Object} o The data object.
1354          * @param {Object} arg The callback argument object passed to the load function.
1355          */
1356         load : true,
1357         /**
1358          * @event loadexception
1359          * Fires if an Exception occurs during data retrieval.
1360          * @param {Object} This DataProxy object.
1361          * @param {Object} o The data object.
1362          * @param {Object} arg The callback argument object passed to the load function.
1363          * @param {Object} e The Exception.
1364          */
1365         loadexception : true
1366     });
1367     Roo.data.DataProxy.superclass.constructor.call(this);
1368 };
1369
1370 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1371
1372     /**
1373      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1374      */
1375 /*
1376  * Based on:
1377  * Ext JS Library 1.1.1
1378  * Copyright(c) 2006-2007, Ext JS, LLC.
1379  *
1380  * Originally Released Under LGPL - original licence link has changed is not relivant.
1381  *
1382  * Fork - LGPL
1383  * <script type="text/javascript">
1384  */
1385 /**
1386  * @class Roo.data.MemoryProxy
1387  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1388  * to the Reader when its load method is called.
1389  * @constructor
1390  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1391  */
1392 Roo.data.MemoryProxy = function(data){
1393     if (data.data) {
1394         data = data.data;
1395     }
1396     Roo.data.MemoryProxy.superclass.constructor.call(this);
1397     this.data = data;
1398 };
1399
1400 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1401     
1402     /**
1403      * Load data from the requested source (in this case an in-memory
1404      * data object passed to the constructor), read the data object into
1405      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1406      * process that block using the passed callback.
1407      * @param {Object} params This parameter is not used by the MemoryProxy class.
1408      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1409      * object into a block of Roo.data.Records.
1410      * @param {Function} callback The function into which to pass the block of Roo.data.records.
1411      * The function must be passed <ul>
1412      * <li>The Record block object</li>
1413      * <li>The "arg" argument from the load function</li>
1414      * <li>A boolean success indicator</li>
1415      * </ul>
1416      * @param {Object} scope The scope in which to call the callback
1417      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1418      */
1419     load : function(params, reader, callback, scope, arg){
1420         params = params || {};
1421         var result;
1422         try {
1423             result = reader.readRecords(params.data ? params.data :this.data);
1424         }catch(e){
1425             this.fireEvent("loadexception", this, arg, null, e);
1426             callback.call(scope, null, arg, false);
1427             return;
1428         }
1429         callback.call(scope, result, arg, true);
1430     },
1431     
1432     // private
1433     update : function(params, records){
1434         
1435     }
1436 });/*
1437  * Based on:
1438  * Ext JS Library 1.1.1
1439  * Copyright(c) 2006-2007, Ext JS, LLC.
1440  *
1441  * Originally Released Under LGPL - original licence link has changed is not relivant.
1442  *
1443  * Fork - LGPL
1444  * <script type="text/javascript">
1445  */
1446 /**
1447  * @class Roo.data.HttpProxy
1448  * @extends Roo.data.DataProxy
1449  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1450  * configured to reference a certain URL.<br><br>
1451  * <p>
1452  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1453  * from which the running page was served.<br><br>
1454  * <p>
1455  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1456  * <p>
1457  * Be aware that to enable the browser to parse an XML document, the server must set
1458  * the Content-Type header in the HTTP response to "text/xml".
1459  * @constructor
1460  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1461  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
1462  * will be used to make the request.
1463  */
1464 Roo.data.HttpProxy = function(conn){
1465     Roo.data.HttpProxy.superclass.constructor.call(this);
1466     // is conn a conn config or a real conn?
1467     this.conn = conn;
1468     this.useAjax = !conn || !conn.events;
1469   
1470 };
1471
1472 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1473     // thse are take from connection...
1474     
1475     /**
1476      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1477      */
1478     /**
1479      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1480      * extra parameters to each request made by this object. (defaults to undefined)
1481      */
1482     /**
1483      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1484      *  to each request made by this object. (defaults to undefined)
1485      */
1486     /**
1487      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
1488      */
1489     /**
1490      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1491      */
1492      /**
1493      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1494      * @type Boolean
1495      */
1496   
1497
1498     /**
1499      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1500      * @type Boolean
1501      */
1502     /**
1503      * Return the {@link Roo.data.Connection} object being used by this Proxy.
1504      * @return {Connection} The Connection object. This object may be used to subscribe to events on
1505      * a finer-grained basis than the DataProxy events.
1506      */
1507     getConnection : function(){
1508         return this.useAjax ? Roo.Ajax : this.conn;
1509     },
1510
1511     /**
1512      * Load data from the configured {@link Roo.data.Connection}, read the data object into
1513      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1514      * process that block using the passed callback.
1515      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1516      * for the request to the remote server.
1517      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1518      * object into a block of Roo.data.Records.
1519      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1520      * The function must be passed <ul>
1521      * <li>The Record block object</li>
1522      * <li>The "arg" argument from the load function</li>
1523      * <li>A boolean success indicator</li>
1524      * </ul>
1525      * @param {Object} scope The scope in which to call the callback
1526      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1527      */
1528     load : function(params, reader, callback, scope, arg){
1529         if(this.fireEvent("beforeload", this, params) !== false){
1530             var  o = {
1531                 params : params || {},
1532                 request: {
1533                     callback : callback,
1534                     scope : scope,
1535                     arg : arg
1536                 },
1537                 reader: reader,
1538                 callback : this.loadResponse,
1539                 scope: this
1540             };
1541             if(this.useAjax){
1542                 Roo.applyIf(o, this.conn);
1543                 if(this.activeRequest){
1544                     Roo.Ajax.abort(this.activeRequest);
1545                 }
1546                 this.activeRequest = Roo.Ajax.request(o);
1547             }else{
1548                 this.conn.request(o);
1549             }
1550         }else{
1551             callback.call(scope||this, null, arg, false);
1552         }
1553     },
1554
1555     // private
1556     loadResponse : function(o, success, response){
1557         delete this.activeRequest;
1558         if(!success){
1559             this.fireEvent("loadexception", this, o, response);
1560             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1561             return;
1562         }
1563         var result;
1564         try {
1565             result = o.reader.read(response);
1566         }catch(e){
1567             o.success = false;
1568             o.raw = { errorMsg : response.responseText };
1569             this.fireEvent("loadexception", this, o, response, e);
1570             o.request.callback.call(o.request.scope, o, o.request.arg, false);
1571             return;
1572         }
1573         
1574         this.fireEvent("load", this, o, o.request.arg);
1575         o.request.callback.call(o.request.scope, result, o.request.arg, true);
1576     },
1577
1578     // private
1579     update : function(dataSet){
1580
1581     },
1582
1583     // private
1584     updateResponse : function(dataSet){
1585
1586     }
1587 });/*
1588  * Based on:
1589  * Ext JS Library 1.1.1
1590  * Copyright(c) 2006-2007, Ext JS, LLC.
1591  *
1592  * Originally Released Under LGPL - original licence link has changed is not relivant.
1593  *
1594  * Fork - LGPL
1595  * <script type="text/javascript">
1596  */
1597
1598 /**
1599  * @class Roo.data.ScriptTagProxy
1600  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1601  * other than the originating domain of the running page.<br><br>
1602  * <p>
1603  * <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
1604  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1605  * <p>
1606  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1607  * source code that is used as the source inside a &lt;script> tag.<br><br>
1608  * <p>
1609  * In order for the browser to process the returned data, the server must wrap the data object
1610  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1611  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1612  * depending on whether the callback name was passed:
1613  * <p>
1614  * <pre><code>
1615 boolean scriptTag = false;
1616 String cb = request.getParameter("callback");
1617 if (cb != null) {
1618     scriptTag = true;
1619     response.setContentType("text/javascript");
1620 } else {
1621     response.setContentType("application/x-json");
1622 }
1623 Writer out = response.getWriter();
1624 if (scriptTag) {
1625     out.write(cb + "(");
1626 }
1627 out.print(dataBlock.toJsonString());
1628 if (scriptTag) {
1629     out.write(");");
1630 }
1631 </pre></code>
1632  *
1633  * @constructor
1634  * @param {Object} config A configuration object.
1635  */
1636 Roo.data.ScriptTagProxy = function(config){
1637     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1638     Roo.apply(this, config);
1639     this.head = document.getElementsByTagName("head")[0];
1640 };
1641
1642 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1643
1644 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1645     /**
1646      * @cfg {String} url The URL from which to request the data object.
1647      */
1648     /**
1649      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1650      */
1651     timeout : 30000,
1652     /**
1653      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1654      * the server the name of the callback function set up by the load call to process the returned data object.
1655      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1656      * javascript output which calls this named function passing the data object as its only parameter.
1657      */
1658     callbackParam : "callback",
1659     /**
1660      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1661      * name to the request.
1662      */
1663     nocache : true,
1664
1665     /**
1666      * Load data from the configured URL, read the data object into
1667      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1668      * process that block using the passed callback.
1669      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1670      * for the request to the remote server.
1671      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1672      * object into a block of Roo.data.Records.
1673      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1674      * The function must be passed <ul>
1675      * <li>The Record block object</li>
1676      * <li>The "arg" argument from the load function</li>
1677      * <li>A boolean success indicator</li>
1678      * </ul>
1679      * @param {Object} scope The scope in which to call the callback
1680      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1681      */
1682     load : function(params, reader, callback, scope, arg){
1683         if(this.fireEvent("beforeload", this, params) !== false){
1684
1685             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1686
1687             var url = this.url;
1688             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1689             if(this.nocache){
1690                 url += "&_dc=" + (new Date().getTime());
1691             }
1692             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1693             var trans = {
1694                 id : transId,
1695                 cb : "stcCallback"+transId,
1696                 scriptId : "stcScript"+transId,
1697                 params : params,
1698                 arg : arg,
1699                 url : url,
1700                 callback : callback,
1701                 scope : scope,
1702                 reader : reader
1703             };
1704             var conn = this;
1705
1706             window[trans.cb] = function(o){
1707                 conn.handleResponse(o, trans);
1708             };
1709
1710             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1711
1712             if(this.autoAbort !== false){
1713                 this.abort();
1714             }
1715
1716             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1717
1718             var script = document.createElement("script");
1719             script.setAttribute("src", url);
1720             script.setAttribute("type", "text/javascript");
1721             script.setAttribute("id", trans.scriptId);
1722             this.head.appendChild(script);
1723
1724             this.trans = trans;
1725         }else{
1726             callback.call(scope||this, null, arg, false);
1727         }
1728     },
1729
1730     // private
1731     isLoading : function(){
1732         return this.trans ? true : false;
1733     },
1734
1735     /**
1736      * Abort the current server request.
1737      */
1738     abort : function(){
1739         if(this.isLoading()){
1740             this.destroyTrans(this.trans);
1741         }
1742     },
1743
1744     // private
1745     destroyTrans : function(trans, isLoaded){
1746         this.head.removeChild(document.getElementById(trans.scriptId));
1747         clearTimeout(trans.timeoutId);
1748         if(isLoaded){
1749             window[trans.cb] = undefined;
1750             try{
1751                 delete window[trans.cb];
1752             }catch(e){}
1753         }else{
1754             // if hasn't been loaded, wait for load to remove it to prevent script error
1755             window[trans.cb] = function(){
1756                 window[trans.cb] = undefined;
1757                 try{
1758                     delete window[trans.cb];
1759                 }catch(e){}
1760             };
1761         }
1762     },
1763
1764     // private
1765     handleResponse : function(o, trans){
1766         this.trans = false;
1767         this.destroyTrans(trans, true);
1768         var result;
1769         try {
1770             result = trans.reader.readRecords(o);
1771         }catch(e){
1772             this.fireEvent("loadexception", this, o, trans.arg, e);
1773             trans.callback.call(trans.scope||window, null, trans.arg, false);
1774             return;
1775         }
1776         this.fireEvent("load", this, o, trans.arg);
1777         trans.callback.call(trans.scope||window, result, trans.arg, true);
1778     },
1779
1780     // private
1781     handleFailure : function(trans){
1782         this.trans = false;
1783         this.destroyTrans(trans, false);
1784         this.fireEvent("loadexception", this, null, trans.arg);
1785         trans.callback.call(trans.scope||window, null, trans.arg, false);
1786     }
1787 });/*
1788  * Based on:
1789  * Ext JS Library 1.1.1
1790  * Copyright(c) 2006-2007, Ext JS, LLC.
1791  *
1792  * Originally Released Under LGPL - original licence link has changed is not relivant.
1793  *
1794  * Fork - LGPL
1795  * <script type="text/javascript">
1796  */
1797
1798 /**
1799  * @class Roo.data.JsonReader
1800  * @extends Roo.data.DataReader
1801  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1802  * based on mappings in a provided Roo.data.Record constructor.
1803  * 
1804  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1805  * in the reply previously. 
1806  * 
1807  * <p>
1808  * Example code:
1809  * <pre><code>
1810 var RecordDef = Roo.data.Record.create([
1811     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1812     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
1813 ]);
1814 var myReader = new Roo.data.JsonReader({
1815     totalProperty: "results",    // The property which contains the total dataset size (optional)
1816     root: "rows",                // The property which contains an Array of row objects
1817     id: "id"                     // The property within each row object that provides an ID for the record (optional)
1818 }, RecordDef);
1819 </code></pre>
1820  * <p>
1821  * This would consume a JSON file like this:
1822  * <pre><code>
1823 { 'results': 2, 'rows': [
1824     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1825     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1826 }
1827 </code></pre>
1828  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1829  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1830  * paged from the remote server.
1831  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1832  * @cfg {String} root name of the property which contains the Array of row objects.
1833  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1834  * @cfg {Array} fields Array of field definition objects
1835  * @constructor
1836  * Create a new JsonReader
1837  * @param {Object} meta Metadata configuration options
1838  * @param {Object} recordType Either an Array of field definition objects,
1839  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1840  */
1841 Roo.data.JsonReader = function(meta, recordType){
1842     
1843     meta = meta || {};
1844     // set some defaults:
1845     Roo.applyIf(meta, {
1846         totalProperty: 'total',
1847         successProperty : 'success',
1848         root : 'data',
1849         id : 'id'
1850     });
1851     
1852     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1853 };
1854 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1855     
1856     readerType : 'Json',
1857     
1858     /**
1859      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
1860      * Used by Store query builder to append _requestMeta to params.
1861      * 
1862      */
1863     metaFromRemote : false,
1864     /**
1865      * This method is only used by a DataProxy which has retrieved data from a remote server.
1866      * @param {Object} response The XHR object which contains the JSON data in its responseText.
1867      * @return {Object} data A data block which is used by an Roo.data.Store object as
1868      * a cache of Roo.data.Records.
1869      */
1870     read : function(response){
1871         var json = response.responseText;
1872        
1873         var o = /* eval:var:o */ eval("("+json+")");
1874         if(!o) {
1875             throw {message: "JsonReader.read: Json object not found"};
1876         }
1877         
1878         if(o.metaData){
1879             
1880             delete this.ef;
1881             this.metaFromRemote = true;
1882             this.meta = o.metaData;
1883             this.recordType = Roo.data.Record.create(o.metaData.fields);
1884             this.onMetaChange(this.meta, this.recordType, o);
1885         }
1886         return this.readRecords(o);
1887     },
1888
1889     // private function a store will implement
1890     onMetaChange : function(meta, recordType, o){
1891
1892     },
1893
1894     /**
1895          * @ignore
1896          */
1897     simpleAccess: function(obj, subsc) {
1898         return obj[subsc];
1899     },
1900
1901         /**
1902          * @ignore
1903          */
1904     getJsonAccessor: function(){
1905         var re = /[\[\.]/;
1906         return function(expr) {
1907             try {
1908                 return(re.test(expr))
1909                     ? new Function("obj", "return obj." + expr)
1910                     : function(obj){
1911                         return obj[expr];
1912                     };
1913             } catch(e){}
1914             return Roo.emptyFn;
1915         };
1916     }(),
1917
1918     /**
1919      * Create a data block containing Roo.data.Records from an XML document.
1920      * @param {Object} o An object which contains an Array of row objects in the property specified
1921      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1922      * which contains the total size of the dataset.
1923      * @return {Object} data A data block which is used by an Roo.data.Store object as
1924      * a cache of Roo.data.Records.
1925      */
1926     readRecords : function(o){
1927         /**
1928          * After any data loads, the raw JSON data is available for further custom processing.
1929          * @type Object
1930          */
1931         this.o = o;
1932         var s = this.meta, Record = this.recordType,
1933             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1934
1935 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
1936         if (!this.ef) {
1937             if(s.totalProperty) {
1938                     this.getTotal = this.getJsonAccessor(s.totalProperty);
1939                 }
1940                 if(s.successProperty) {
1941                     this.getSuccess = this.getJsonAccessor(s.successProperty);
1942                 }
1943                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1944                 if (s.id) {
1945                         var g = this.getJsonAccessor(s.id);
1946                         this.getId = function(rec) {
1947                                 var r = g(rec);  
1948                                 return (r === undefined || r === "") ? null : r;
1949                         };
1950                 } else {
1951                         this.getId = function(){return null;};
1952                 }
1953             this.ef = [];
1954             for(var jj = 0; jj < fl; jj++){
1955                 f = fi[jj];
1956                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1957                 this.ef[jj] = this.getJsonAccessor(map);
1958             }
1959         }
1960
1961         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1962         if(s.totalProperty){
1963             var vt = parseInt(this.getTotal(o), 10);
1964             if(!isNaN(vt)){
1965                 totalRecords = vt;
1966             }
1967         }
1968         if(s.successProperty){
1969             var vs = this.getSuccess(o);
1970             if(vs === false || vs === 'false'){
1971                 success = false;
1972             }
1973         }
1974         var records = [];
1975         for(var i = 0; i < c; i++){
1976                 var n = root[i];
1977             var values = {};
1978             var id = this.getId(n);
1979             for(var j = 0; j < fl; j++){
1980                 f = fi[j];
1981             var v = this.ef[j](n);
1982             if (!f.convert) {
1983                 Roo.log('missing convert for ' + f.name);
1984                 Roo.log(f);
1985                 continue;
1986             }
1987             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1988             }
1989             var record = new Record(values, id);
1990             record.json = n;
1991             records[i] = record;
1992         }
1993         return {
1994             raw : o,
1995             success : success,
1996             records : records,
1997             totalRecords : totalRecords
1998         };
1999     },
2000     // used when loading children.. @see loadDataFromChildren
2001     toLoadData: function(rec)
2002     {
2003         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2004         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2005         return { data : data, total : data.length };
2006         
2007     }
2008 });/*
2009  * Based on:
2010  * Ext JS Library 1.1.1
2011  * Copyright(c) 2006-2007, Ext JS, LLC.
2012  *
2013  * Originally Released Under LGPL - original licence link has changed is not relivant.
2014  *
2015  * Fork - LGPL
2016  * <script type="text/javascript">
2017  */
2018
2019 /**
2020  * @class Roo.data.XmlReader
2021  * @extends Roo.data.DataReader
2022  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
2023  * based on mappings in a provided Roo.data.Record constructor.<br><br>
2024  * <p>
2025  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
2026  * header in the HTTP response must be set to "text/xml".</em>
2027  * <p>
2028  * Example code:
2029  * <pre><code>
2030 var RecordDef = Roo.data.Record.create([
2031    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
2032    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
2033 ]);
2034 var myReader = new Roo.data.XmlReader({
2035    totalRecords: "results", // The element which contains the total dataset size (optional)
2036    record: "row",           // The repeated element which contains row information
2037    id: "id"                 // The element within the row that provides an ID for the record (optional)
2038 }, RecordDef);
2039 </code></pre>
2040  * <p>
2041  * This would consume an XML file like this:
2042  * <pre><code>
2043 &lt;?xml?>
2044 &lt;dataset>
2045  &lt;results>2&lt;/results>
2046  &lt;row>
2047    &lt;id>1&lt;/id>
2048    &lt;name>Bill&lt;/name>
2049    &lt;occupation>Gardener&lt;/occupation>
2050  &lt;/row>
2051  &lt;row>
2052    &lt;id>2&lt;/id>
2053    &lt;name>Ben&lt;/name>
2054    &lt;occupation>Horticulturalist&lt;/occupation>
2055  &lt;/row>
2056 &lt;/dataset>
2057 </code></pre>
2058  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2059  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2060  * paged from the remote server.
2061  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2062  * @cfg {String} success The DomQuery path to the success attribute used by forms.
2063  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2064  * a record identifier value.
2065  * @constructor
2066  * Create a new XmlReader
2067  * @param {Object} meta Metadata configuration options
2068  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
2069  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2070  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
2071  */
2072 Roo.data.XmlReader = function(meta, recordType){
2073     meta = meta || {};
2074     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2075 };
2076 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2077     
2078     readerType : 'Xml',
2079     
2080     /**
2081      * This method is only used by a DataProxy which has retrieved data from a remote server.
2082          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
2083          * to contain a method called 'responseXML' that returns an XML document object.
2084      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2085      * a cache of Roo.data.Records.
2086      */
2087     read : function(response){
2088         var doc = response.responseXML;
2089         if(!doc) {
2090             throw {message: "XmlReader.read: XML Document not available"};
2091         }
2092         return this.readRecords(doc);
2093     },
2094
2095     /**
2096      * Create a data block containing Roo.data.Records from an XML document.
2097          * @param {Object} doc A parsed XML document.
2098      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2099      * a cache of Roo.data.Records.
2100      */
2101     readRecords : function(doc){
2102         /**
2103          * After any data loads/reads, the raw XML Document is available for further custom processing.
2104          * @type XMLDocument
2105          */
2106         this.xmlData = doc;
2107         var root = doc.documentElement || doc;
2108         var q = Roo.DomQuery;
2109         var recordType = this.recordType, fields = recordType.prototype.fields;
2110         var sid = this.meta.id;
2111         var totalRecords = 0, success = true;
2112         if(this.meta.totalRecords){
2113             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2114         }
2115         
2116         if(this.meta.success){
2117             var sv = q.selectValue(this.meta.success, root, true);
2118             success = sv !== false && sv !== 'false';
2119         }
2120         var records = [];
2121         var ns = q.select(this.meta.record, root);
2122         for(var i = 0, len = ns.length; i < len; i++) {
2123                 var n = ns[i];
2124                 var values = {};
2125                 var id = sid ? q.selectValue(sid, n) : undefined;
2126                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2127                     var f = fields.items[j];
2128                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2129                     v = f.convert(v);
2130                     values[f.name] = v;
2131                 }
2132                 var record = new recordType(values, id);
2133                 record.node = n;
2134                 records[records.length] = record;
2135             }
2136
2137             return {
2138                 success : success,
2139                 records : records,
2140                 totalRecords : totalRecords || records.length
2141             };
2142     }
2143 });/*
2144  * Based on:
2145  * Ext JS Library 1.1.1
2146  * Copyright(c) 2006-2007, Ext JS, LLC.
2147  *
2148  * Originally Released Under LGPL - original licence link has changed is not relivant.
2149  *
2150  * Fork - LGPL
2151  * <script type="text/javascript">
2152  */
2153
2154 /**
2155  * @class Roo.data.ArrayReader
2156  * @extends Roo.data.DataReader
2157  * Data reader class to create an Array of Roo.data.Record objects from an Array.
2158  * Each element of that Array represents a row of data fields. The
2159  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2160  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2161  * <p>
2162  * Example code:.
2163  * <pre><code>
2164 var RecordDef = Roo.data.Record.create([
2165     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
2166     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
2167 ]);
2168 var myReader = new Roo.data.ArrayReader({
2169     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
2170 }, RecordDef);
2171 </code></pre>
2172  * <p>
2173  * This would consume an Array like this:
2174  * <pre><code>
2175 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2176   </code></pre>
2177  
2178  * @constructor
2179  * Create a new JsonReader
2180  * @param {Object} meta Metadata configuration options.
2181  * @param {Object|Array} recordType Either an Array of field definition objects
2182  * 
2183  * @cfg {Array} fields Array of field definition objects
2184  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2185  * as specified to {@link Roo.data.Record#create},
2186  * or an {@link Roo.data.Record} object
2187  *
2188  * 
2189  * created using {@link Roo.data.Record#create}.
2190  */
2191 Roo.data.ArrayReader = function(meta, recordType)
2192 {    
2193     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2194 };
2195
2196 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2197     
2198       /**
2199      * Create a data block containing Roo.data.Records from an XML document.
2200      * @param {Object} o An Array of row objects which represents the dataset.
2201      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2202      * a cache of Roo.data.Records.
2203      */
2204     readRecords : function(o)
2205     {
2206         var sid = this.meta ? this.meta.id : null;
2207         var recordType = this.recordType, fields = recordType.prototype.fields;
2208         var records = [];
2209         var root = o;
2210         for(var i = 0; i < root.length; i++){
2211             var n = root[i];
2212             var values = {};
2213             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2214             for(var j = 0, jlen = fields.length; j < jlen; j++){
2215                 var f = fields.items[j];
2216                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2217                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2218                 v = f.convert(v);
2219                 values[f.name] = v;
2220             }
2221             var record = new recordType(values, id);
2222             record.json = n;
2223             records[records.length] = record;
2224         }
2225         return {
2226             records : records,
2227             totalRecords : records.length
2228         };
2229     },
2230     // used when loading children.. @see loadDataFromChildren
2231     toLoadData: function(rec)
2232     {
2233         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2234         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2235         
2236     }
2237     
2238     
2239 });/*
2240  * Based on:
2241  * Ext JS Library 1.1.1
2242  * Copyright(c) 2006-2007, Ext JS, LLC.
2243  *
2244  * Originally Released Under LGPL - original licence link has changed is not relivant.
2245  *
2246  * Fork - LGPL
2247  * <script type="text/javascript">
2248  */
2249
2250
2251 /**
2252  * @class Roo.data.Tree
2253  * @extends Roo.util.Observable
2254  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2255  * in the tree have most standard DOM functionality.
2256  * @constructor
2257  * @param {Node} root (optional) The root node
2258  */
2259 Roo.data.Tree = function(root){
2260    this.nodeHash = {};
2261    /**
2262     * The root node for this tree
2263     * @type Node
2264     */
2265    this.root = null;
2266    if(root){
2267        this.setRootNode(root);
2268    }
2269    this.addEvents({
2270        /**
2271         * @event append
2272         * Fires when a new child node is appended to a node in this tree.
2273         * @param {Tree} tree The owner tree
2274         * @param {Node} parent The parent node
2275         * @param {Node} node The newly appended node
2276         * @param {Number} index The index of the newly appended node
2277         */
2278        "append" : true,
2279        /**
2280         * @event remove
2281         * Fires when a child node is removed from a node in this tree.
2282         * @param {Tree} tree The owner tree
2283         * @param {Node} parent The parent node
2284         * @param {Node} node The child node removed
2285         */
2286        "remove" : true,
2287        /**
2288         * @event move
2289         * Fires when a node is moved to a new location in the tree
2290         * @param {Tree} tree The owner tree
2291         * @param {Node} node The node moved
2292         * @param {Node} oldParent The old parent of this node
2293         * @param {Node} newParent The new parent of this node
2294         * @param {Number} index The index it was moved to
2295         */
2296        "move" : true,
2297        /**
2298         * @event insert
2299         * Fires when a new child node is inserted in a node in this tree.
2300         * @param {Tree} tree The owner tree
2301         * @param {Node} parent The parent node
2302         * @param {Node} node The child node inserted
2303         * @param {Node} refNode The child node the node was inserted before
2304         */
2305        "insert" : true,
2306        /**
2307         * @event beforeappend
2308         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2309         * @param {Tree} tree The owner tree
2310         * @param {Node} parent The parent node
2311         * @param {Node} node The child node to be appended
2312         */
2313        "beforeappend" : true,
2314        /**
2315         * @event beforeremove
2316         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2317         * @param {Tree} tree The owner tree
2318         * @param {Node} parent The parent node
2319         * @param {Node} node The child node to be removed
2320         */
2321        "beforeremove" : true,
2322        /**
2323         * @event beforemove
2324         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2325         * @param {Tree} tree The owner tree
2326         * @param {Node} node The node being moved
2327         * @param {Node} oldParent The parent of the node
2328         * @param {Node} newParent The new parent the node is moving to
2329         * @param {Number} index The index it is being moved to
2330         */
2331        "beforemove" : true,
2332        /**
2333         * @event beforeinsert
2334         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2335         * @param {Tree} tree The owner tree
2336         * @param {Node} parent The parent node
2337         * @param {Node} node The child node to be inserted
2338         * @param {Node} refNode The child node the node is being inserted before
2339         */
2340        "beforeinsert" : true
2341    });
2342
2343     Roo.data.Tree.superclass.constructor.call(this);
2344 };
2345
2346 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2347     pathSeparator: "/",
2348
2349     proxyNodeEvent : function(){
2350         return this.fireEvent.apply(this, arguments);
2351     },
2352
2353     /**
2354      * Returns the root node for this tree.
2355      * @return {Node}
2356      */
2357     getRootNode : function(){
2358         return this.root;
2359     },
2360
2361     /**
2362      * Sets the root node for this tree.
2363      * @param {Node} node
2364      * @return {Node}
2365      */
2366     setRootNode : function(node){
2367         this.root = node;
2368         node.ownerTree = this;
2369         node.isRoot = true;
2370         this.registerNode(node);
2371         return node;
2372     },
2373
2374     /**
2375      * Gets a node in this tree by its id.
2376      * @param {String} id
2377      * @return {Node}
2378      */
2379     getNodeById : function(id){
2380         return this.nodeHash[id];
2381     },
2382
2383     registerNode : function(node){
2384         this.nodeHash[node.id] = node;
2385     },
2386
2387     unregisterNode : function(node){
2388         delete this.nodeHash[node.id];
2389     },
2390
2391     toString : function(){
2392         return "[Tree"+(this.id?" "+this.id:"")+"]";
2393     }
2394 });
2395
2396 /**
2397  * @class Roo.data.Node
2398  * @extends Roo.util.Observable
2399  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2400  * @cfg {String} id The id for this node. If one is not specified, one is generated.
2401  * @constructor
2402  * @param {Object} attributes The attributes/config for the node
2403  */
2404 Roo.data.Node = function(attributes){
2405     /**
2406      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2407      * @type {Object}
2408      */
2409     this.attributes = attributes || {};
2410     this.leaf = this.attributes.leaf;
2411     /**
2412      * The node id. @type String
2413      */
2414     this.id = this.attributes.id;
2415     if(!this.id){
2416         this.id = Roo.id(null, "ynode-");
2417         this.attributes.id = this.id;
2418     }
2419      
2420     
2421     /**
2422      * All child nodes of this node. @type Array
2423      */
2424     this.childNodes = [];
2425     if(!this.childNodes.indexOf){ // indexOf is a must
2426         this.childNodes.indexOf = function(o){
2427             for(var i = 0, len = this.length; i < len; i++){
2428                 if(this[i] == o) {
2429                     return i;
2430                 }
2431             }
2432             return -1;
2433         };
2434     }
2435     /**
2436      * The parent node for this node. @type Node
2437      */
2438     this.parentNode = null;
2439     /**
2440      * The first direct child node of this node, or null if this node has no child nodes. @type Node
2441      */
2442     this.firstChild = null;
2443     /**
2444      * The last direct child node of this node, or null if this node has no child nodes. @type Node
2445      */
2446     this.lastChild = null;
2447     /**
2448      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2449      */
2450     this.previousSibling = null;
2451     /**
2452      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2453      */
2454     this.nextSibling = null;
2455
2456     this.addEvents({
2457        /**
2458         * @event append
2459         * Fires when a new child node is appended
2460         * @param {Tree} tree The owner tree
2461         * @param {Node} this This node
2462         * @param {Node} node The newly appended node
2463         * @param {Number} index The index of the newly appended node
2464         */
2465        "append" : true,
2466        /**
2467         * @event remove
2468         * Fires when a child node is removed
2469         * @param {Tree} tree The owner tree
2470         * @param {Node} this This node
2471         * @param {Node} node The removed node
2472         */
2473        "remove" : true,
2474        /**
2475         * @event move
2476         * Fires when this node is moved to a new location in the tree
2477         * @param {Tree} tree The owner tree
2478         * @param {Node} this This node
2479         * @param {Node} oldParent The old parent of this node
2480         * @param {Node} newParent The new parent of this node
2481         * @param {Number} index The index it was moved to
2482         */
2483        "move" : true,
2484        /**
2485         * @event insert
2486         * Fires when a new child node is inserted.
2487         * @param {Tree} tree The owner tree
2488         * @param {Node} this This node
2489         * @param {Node} node The child node inserted
2490         * @param {Node} refNode The child node the node was inserted before
2491         */
2492        "insert" : true,
2493        /**
2494         * @event beforeappend
2495         * Fires before a new child is appended, return false to cancel the append.
2496         * @param {Tree} tree The owner tree
2497         * @param {Node} this This node
2498         * @param {Node} node The child node to be appended
2499         */
2500        "beforeappend" : true,
2501        /**
2502         * @event beforeremove
2503         * Fires before a child is removed, return false to cancel the remove.
2504         * @param {Tree} tree The owner tree
2505         * @param {Node} this This node
2506         * @param {Node} node The child node to be removed
2507         */
2508        "beforeremove" : true,
2509        /**
2510         * @event beforemove
2511         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2512         * @param {Tree} tree The owner tree
2513         * @param {Node} this This node
2514         * @param {Node} oldParent The parent of this node
2515         * @param {Node} newParent The new parent this node is moving to
2516         * @param {Number} index The index it is being moved to
2517         */
2518        "beforemove" : true,
2519        /**
2520         * @event beforeinsert
2521         * Fires before a new child is inserted, return false to cancel the insert.
2522         * @param {Tree} tree The owner tree
2523         * @param {Node} this This node
2524         * @param {Node} node The child node to be inserted
2525         * @param {Node} refNode The child node the node is being inserted before
2526         */
2527        "beforeinsert" : true
2528    });
2529     this.listeners = this.attributes.listeners;
2530     Roo.data.Node.superclass.constructor.call(this);
2531 };
2532
2533 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2534     fireEvent : function(evtName){
2535         // first do standard event for this node
2536         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2537             return false;
2538         }
2539         // then bubble it up to the tree if the event wasn't cancelled
2540         var ot = this.getOwnerTree();
2541         if(ot){
2542             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2543                 return false;
2544             }
2545         }
2546         return true;
2547     },
2548
2549     /**
2550      * Returns true if this node is a leaf
2551      * @return {Boolean}
2552      */
2553     isLeaf : function(){
2554         return this.leaf === true;
2555     },
2556
2557     // private
2558     setFirstChild : function(node){
2559         this.firstChild = node;
2560     },
2561
2562     //private
2563     setLastChild : function(node){
2564         this.lastChild = node;
2565     },
2566
2567
2568     /**
2569      * Returns true if this node is the last child of its parent
2570      * @return {Boolean}
2571      */
2572     isLast : function(){
2573        return (!this.parentNode ? true : this.parentNode.lastChild == this);
2574     },
2575
2576     /**
2577      * Returns true if this node is the first child of its parent
2578      * @return {Boolean}
2579      */
2580     isFirst : function(){
2581        return (!this.parentNode ? true : this.parentNode.firstChild == this);
2582     },
2583
2584     hasChildNodes : function(){
2585         return !this.isLeaf() && this.childNodes.length > 0;
2586     },
2587
2588     /**
2589      * Insert node(s) as the last child node of this node.
2590      * @param {Node/Array} node The node or Array of nodes to append
2591      * @return {Node} The appended node if single append, or null if an array was passed
2592      */
2593     appendChild : function(node){
2594         var multi = false;
2595         if(node instanceof Array){
2596             multi = node;
2597         }else if(arguments.length > 1){
2598             multi = arguments;
2599         }
2600         
2601         // if passed an array or multiple args do them one by one
2602         if(multi){
2603             for(var i = 0, len = multi.length; i < len; i++) {
2604                 this.appendChild(multi[i]);
2605             }
2606         }else{
2607             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2608                 return false;
2609             }
2610             var index = this.childNodes.length;
2611             var oldParent = node.parentNode;
2612             // it's a move, make sure we move it cleanly
2613             if(oldParent){
2614                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2615                     return false;
2616                 }
2617                 oldParent.removeChild(node);
2618             }
2619             
2620             index = this.childNodes.length;
2621             if(index == 0){
2622                 this.setFirstChild(node);
2623             }
2624             this.childNodes.push(node);
2625             node.parentNode = this;
2626             var ps = this.childNodes[index-1];
2627             if(ps){
2628                 node.previousSibling = ps;
2629                 ps.nextSibling = node;
2630             }else{
2631                 node.previousSibling = null;
2632             }
2633             node.nextSibling = null;
2634             this.setLastChild(node);
2635             node.setOwnerTree(this.getOwnerTree());
2636             this.fireEvent("append", this.ownerTree, this, node, index);
2637             if(this.ownerTree) {
2638                 this.ownerTree.fireEvent("appendnode", this, node, index);
2639             }
2640             if(oldParent){
2641                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2642             }
2643             return node;
2644         }
2645     },
2646
2647     /**
2648      * Removes a child node from this node.
2649      * @param {Node} node The node to remove
2650      * @return {Node} The removed node
2651      */
2652     removeChild : function(node){
2653         var index = this.childNodes.indexOf(node);
2654         if(index == -1){
2655             return false;
2656         }
2657         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2658             return false;
2659         }
2660
2661         // remove it from childNodes collection
2662         this.childNodes.splice(index, 1);
2663
2664         // update siblings
2665         if(node.previousSibling){
2666             node.previousSibling.nextSibling = node.nextSibling;
2667         }
2668         if(node.nextSibling){
2669             node.nextSibling.previousSibling = node.previousSibling;
2670         }
2671
2672         // update child refs
2673         if(this.firstChild == node){
2674             this.setFirstChild(node.nextSibling);
2675         }
2676         if(this.lastChild == node){
2677             this.setLastChild(node.previousSibling);
2678         }
2679
2680         node.setOwnerTree(null);
2681         // clear any references from the node
2682         node.parentNode = null;
2683         node.previousSibling = null;
2684         node.nextSibling = null;
2685         this.fireEvent("remove", this.ownerTree, this, node);
2686         return node;
2687     },
2688
2689     /**
2690      * Inserts the first node before the second node in this nodes childNodes collection.
2691      * @param {Node} node The node to insert
2692      * @param {Node} refNode The node to insert before (if null the node is appended)
2693      * @return {Node} The inserted node
2694      */
2695     insertBefore : function(node, refNode){
2696         if(!refNode){ // like standard Dom, refNode can be null for append
2697             return this.appendChild(node);
2698         }
2699         // nothing to do
2700         if(node == refNode){
2701             return false;
2702         }
2703
2704         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2705             return false;
2706         }
2707         var index = this.childNodes.indexOf(refNode);
2708         var oldParent = node.parentNode;
2709         var refIndex = index;
2710
2711         // when moving internally, indexes will change after remove
2712         if(oldParent == this && this.childNodes.indexOf(node) < index){
2713             refIndex--;
2714         }
2715
2716         // it's a move, make sure we move it cleanly
2717         if(oldParent){
2718             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2719                 return false;
2720             }
2721             oldParent.removeChild(node);
2722         }
2723         if(refIndex == 0){
2724             this.setFirstChild(node);
2725         }
2726         this.childNodes.splice(refIndex, 0, node);
2727         node.parentNode = this;
2728         var ps = this.childNodes[refIndex-1];
2729         if(ps){
2730             node.previousSibling = ps;
2731             ps.nextSibling = node;
2732         }else{
2733             node.previousSibling = null;
2734         }
2735         node.nextSibling = refNode;
2736         refNode.previousSibling = node;
2737         node.setOwnerTree(this.getOwnerTree());
2738         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2739         if(oldParent){
2740             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2741         }
2742         return node;
2743     },
2744
2745     /**
2746      * Returns the child node at the specified index.
2747      * @param {Number} index
2748      * @return {Node}
2749      */
2750     item : function(index){
2751         return this.childNodes[index];
2752     },
2753
2754     /**
2755      * Replaces one child node in this node with another.
2756      * @param {Node} newChild The replacement node
2757      * @param {Node} oldChild The node to replace
2758      * @return {Node} The replaced node
2759      */
2760     replaceChild : function(newChild, oldChild){
2761         this.insertBefore(newChild, oldChild);
2762         this.removeChild(oldChild);
2763         return oldChild;
2764     },
2765
2766     /**
2767      * Returns the index of a child node
2768      * @param {Node} node
2769      * @return {Number} The index of the node or -1 if it was not found
2770      */
2771     indexOf : function(child){
2772         return this.childNodes.indexOf(child);
2773     },
2774
2775     /**
2776      * Returns the tree this node is in.
2777      * @return {Tree}
2778      */
2779     getOwnerTree : function(){
2780         // if it doesn't have one, look for one
2781         if(!this.ownerTree){
2782             var p = this;
2783             while(p){
2784                 if(p.ownerTree){
2785                     this.ownerTree = p.ownerTree;
2786                     break;
2787                 }
2788                 p = p.parentNode;
2789             }
2790         }
2791         return this.ownerTree;
2792     },
2793
2794     /**
2795      * Returns depth of this node (the root node has a depth of 0)
2796      * @return {Number}
2797      */
2798     getDepth : function(){
2799         var depth = 0;
2800         var p = this;
2801         while(p.parentNode){
2802             ++depth;
2803             p = p.parentNode;
2804         }
2805         return depth;
2806     },
2807
2808     // private
2809     setOwnerTree : function(tree){
2810         // if it's move, we need to update everyone
2811         if(tree != this.ownerTree){
2812             if(this.ownerTree){
2813                 this.ownerTree.unregisterNode(this);
2814             }
2815             this.ownerTree = tree;
2816             var cs = this.childNodes;
2817             for(var i = 0, len = cs.length; i < len; i++) {
2818                 cs[i].setOwnerTree(tree);
2819             }
2820             if(tree){
2821                 tree.registerNode(this);
2822             }
2823         }
2824     },
2825
2826     /**
2827      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2828      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2829      * @return {String} The path
2830      */
2831     getPath : function(attr){
2832         attr = attr || "id";
2833         var p = this.parentNode;
2834         var b = [this.attributes[attr]];
2835         while(p){
2836             b.unshift(p.attributes[attr]);
2837             p = p.parentNode;
2838         }
2839         var sep = this.getOwnerTree().pathSeparator;
2840         return sep + b.join(sep);
2841     },
2842
2843     /**
2844      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2845      * function call will be the scope provided or the current node. The arguments to the function
2846      * will be the args provided or the current node. If the function returns false at any point,
2847      * the bubble is stopped.
2848      * @param {Function} fn The function to call
2849      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2850      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2851      */
2852     bubble : function(fn, scope, args){
2853         var p = this;
2854         while(p){
2855             if(fn.call(scope || p, args || p) === false){
2856                 break;
2857             }
2858             p = p.parentNode;
2859         }
2860     },
2861
2862     /**
2863      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2864      * function call will be the scope provided or the current node. The arguments to the function
2865      * will be the args provided or the current node. If the function returns false at any point,
2866      * the cascade is stopped on that branch.
2867      * @param {Function} fn The function to call
2868      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2869      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2870      */
2871     cascade : function(fn, scope, args){
2872         if(fn.call(scope || this, args || this) !== false){
2873             var cs = this.childNodes;
2874             for(var i = 0, len = cs.length; i < len; i++) {
2875                 cs[i].cascade(fn, scope, args);
2876             }
2877         }
2878     },
2879
2880     /**
2881      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2882      * function call will be the scope provided or the current node. The arguments to the function
2883      * will be the args provided or the current node. If the function returns false at any point,
2884      * the iteration stops.
2885      * @param {Function} fn The function to call
2886      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2887      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2888      */
2889     eachChild : function(fn, scope, args){
2890         var cs = this.childNodes;
2891         for(var i = 0, len = cs.length; i < len; i++) {
2892                 if(fn.call(scope || this, args || cs[i]) === false){
2893                     break;
2894                 }
2895         }
2896     },
2897
2898     /**
2899      * Finds the first child that has the attribute with the specified value.
2900      * @param {String} attribute The attribute name
2901      * @param {Mixed} value The value to search for
2902      * @return {Node} The found child or null if none was found
2903      */
2904     findChild : function(attribute, value){
2905         var cs = this.childNodes;
2906         for(var i = 0, len = cs.length; i < len; i++) {
2907                 if(cs[i].attributes[attribute] == value){
2908                     return cs[i];
2909                 }
2910         }
2911         return null;
2912     },
2913
2914     /**
2915      * Finds the first child by a custom function. The child matches if the function passed
2916      * returns true.
2917      * @param {Function} fn
2918      * @param {Object} scope (optional)
2919      * @return {Node} The found child or null if none was found
2920      */
2921     findChildBy : function(fn, scope){
2922         var cs = this.childNodes;
2923         for(var i = 0, len = cs.length; i < len; i++) {
2924                 if(fn.call(scope||cs[i], cs[i]) === true){
2925                     return cs[i];
2926                 }
2927         }
2928         return null;
2929     },
2930
2931     /**
2932      * Sorts this nodes children using the supplied sort function
2933      * @param {Function} fn
2934      * @param {Object} scope (optional)
2935      */
2936     sort : function(fn, scope){
2937         var cs = this.childNodes;
2938         var len = cs.length;
2939         if(len > 0){
2940             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2941             cs.sort(sortFn);
2942             for(var i = 0; i < len; i++){
2943                 var n = cs[i];
2944                 n.previousSibling = cs[i-1];
2945                 n.nextSibling = cs[i+1];
2946                 if(i == 0){
2947                     this.setFirstChild(n);
2948                 }
2949                 if(i == len-1){
2950                     this.setLastChild(n);
2951                 }
2952             }
2953         }
2954     },
2955
2956     /**
2957      * Returns true if this node is an ancestor (at any point) of the passed node.
2958      * @param {Node} node
2959      * @return {Boolean}
2960      */
2961     contains : function(node){
2962         return node.isAncestor(this);
2963     },
2964
2965     /**
2966      * Returns true if the passed node is an ancestor (at any point) of this node.
2967      * @param {Node} node
2968      * @return {Boolean}
2969      */
2970     isAncestor : function(node){
2971         var p = this.parentNode;
2972         while(p){
2973             if(p == node){
2974                 return true;
2975             }
2976             p = p.parentNode;
2977         }
2978         return false;
2979     },
2980
2981     toString : function(){
2982         return "[Node"+(this.id?" "+this.id:"")+"]";
2983     }
2984 });/*
2985  * Based on:
2986  * Ext JS Library 1.1.1
2987  * Copyright(c) 2006-2007, Ext JS, LLC.
2988  *
2989  * Originally Released Under LGPL - original licence link has changed is not relivant.
2990  *
2991  * Fork - LGPL
2992  * <script type="text/javascript">
2993  */
2994
2995
2996 /**
2997  * @class Roo.Shadow
2998  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
2999  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
3000  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3001  * @constructor
3002  * Create a new Shadow
3003  * @param {Object} config The config object
3004  */
3005 Roo.Shadow = function(config){
3006     Roo.apply(this, config);
3007     if(typeof this.mode != "string"){
3008         this.mode = this.defaultMode;
3009     }
3010     var o = this.offset, a = {h: 0};
3011     var rad = Math.floor(this.offset/2);
3012     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3013         case "drop":
3014             a.w = 0;
3015             a.l = a.t = o;
3016             a.t -= 1;
3017             if(Roo.isIE){
3018                 a.l -= this.offset + rad;
3019                 a.t -= this.offset + rad;
3020                 a.w -= rad;
3021                 a.h -= rad;
3022                 a.t += 1;
3023             }
3024         break;
3025         case "sides":
3026             a.w = (o*2);
3027             a.l = -o;
3028             a.t = o-1;
3029             if(Roo.isIE){
3030                 a.l -= (this.offset - rad);
3031                 a.t -= this.offset + rad;
3032                 a.l += 1;
3033                 a.w -= (this.offset - rad)*2;
3034                 a.w -= rad + 1;
3035                 a.h -= 1;
3036             }
3037         break;
3038         case "frame":
3039             a.w = a.h = (o*2);
3040             a.l = a.t = -o;
3041             a.t += 1;
3042             a.h -= 2;
3043             if(Roo.isIE){
3044                 a.l -= (this.offset - rad);
3045                 a.t -= (this.offset - rad);
3046                 a.l += 1;
3047                 a.w -= (this.offset + rad + 1);
3048                 a.h -= (this.offset + rad);
3049                 a.h += 1;
3050             }
3051         break;
3052     };
3053
3054     this.adjusts = a;
3055 };
3056
3057 Roo.Shadow.prototype = {
3058     /**
3059      * @cfg {String} mode
3060      * The shadow display mode.  Supports the following options:<br />
3061      * sides: Shadow displays on both sides and bottom only<br />
3062      * frame: Shadow displays equally on all four sides<br />
3063      * drop: Traditional bottom-right drop shadow (default)
3064      */
3065     mode: false,
3066     /**
3067      * @cfg {String} offset
3068      * The number of pixels to offset the shadow from the element (defaults to 4)
3069      */
3070     offset: 4,
3071
3072     // private
3073     defaultMode: "drop",
3074
3075     /**
3076      * Displays the shadow under the target element
3077      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3078      */
3079     show : function(target){
3080         target = Roo.get(target);
3081         if(!this.el){
3082             this.el = Roo.Shadow.Pool.pull();
3083             if(this.el.dom.nextSibling != target.dom){
3084                 this.el.insertBefore(target);
3085             }
3086         }
3087         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3088         if(Roo.isIE){
3089             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3090         }
3091         this.realign(
3092             target.getLeft(true),
3093             target.getTop(true),
3094             target.getWidth(),
3095             target.getHeight()
3096         );
3097         this.el.dom.style.display = "block";
3098     },
3099
3100     /**
3101      * Returns true if the shadow is visible, else false
3102      */
3103     isVisible : function(){
3104         return this.el ? true : false;  
3105     },
3106
3107     /**
3108      * Direct alignment when values are already available. Show must be called at least once before
3109      * calling this method to ensure it is initialized.
3110      * @param {Number} left The target element left position
3111      * @param {Number} top The target element top position
3112      * @param {Number} width The target element width
3113      * @param {Number} height The target element height
3114      */
3115     realign : function(l, t, w, h){
3116         if(!this.el){
3117             return;
3118         }
3119         var a = this.adjusts, d = this.el.dom, s = d.style;
3120         var iea = 0;
3121         s.left = (l+a.l)+"px";
3122         s.top = (t+a.t)+"px";
3123         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3124  
3125         if(s.width != sws || s.height != shs){
3126             s.width = sws;
3127             s.height = shs;
3128             if(!Roo.isIE){
3129                 var cn = d.childNodes;
3130                 var sww = Math.max(0, (sw-12))+"px";
3131                 cn[0].childNodes[1].style.width = sww;
3132                 cn[1].childNodes[1].style.width = sww;
3133                 cn[2].childNodes[1].style.width = sww;
3134                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3135             }
3136         }
3137     },
3138
3139     /**
3140      * Hides this shadow
3141      */
3142     hide : function(){
3143         if(this.el){
3144             this.el.dom.style.display = "none";
3145             Roo.Shadow.Pool.push(this.el);
3146             delete this.el;
3147         }
3148     },
3149
3150     /**
3151      * Adjust the z-index of this shadow
3152      * @param {Number} zindex The new z-index
3153      */
3154     setZIndex : function(z){
3155         this.zIndex = z;
3156         if(this.el){
3157             this.el.setStyle("z-index", z);
3158         }
3159     }
3160 };
3161
3162 // Private utility class that manages the internal Shadow cache
3163 Roo.Shadow.Pool = function(){
3164     var p = [];
3165     var markup = Roo.isIE ?
3166                  '<div class="x-ie-shadow"></div>' :
3167                  '<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>';
3168     return {
3169         pull : function(){
3170             var sh = p.shift();
3171             if(!sh){
3172                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3173                 sh.autoBoxAdjust = false;
3174             }
3175             return sh;
3176         },
3177
3178         push : function(sh){
3179             p.push(sh);
3180         }
3181     };
3182 }();/*
3183  * Based on:
3184  * Ext JS Library 1.1.1
3185  * Copyright(c) 2006-2007, Ext JS, LLC.
3186  *
3187  * Originally Released Under LGPL - original licence link has changed is not relivant.
3188  *
3189  * Fork - LGPL
3190  * <script type="text/javascript">
3191  */
3192
3193
3194 /**
3195  * @class Roo.SplitBar
3196  * @extends Roo.util.Observable
3197  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3198  * <br><br>
3199  * Usage:
3200  * <pre><code>
3201 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3202                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3203 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3204 split.minSize = 100;
3205 split.maxSize = 600;
3206 split.animate = true;
3207 split.on('moved', splitterMoved);
3208 </code></pre>
3209  * @constructor
3210  * Create a new SplitBar
3211  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3212  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3213  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3214  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3215                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3216                         position of the SplitBar).
3217  */
3218 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3219     
3220     /** @private */
3221     this.el = Roo.get(dragElement, true);
3222     this.el.dom.unselectable = "on";
3223     /** @private */
3224     this.resizingEl = Roo.get(resizingElement, true);
3225
3226     /**
3227      * @private
3228      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3229      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3230      * @type Number
3231      */
3232     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3233     
3234     /**
3235      * The minimum size of the resizing element. (Defaults to 0)
3236      * @type Number
3237      */
3238     this.minSize = 0;
3239     
3240     /**
3241      * The maximum size of the resizing element. (Defaults to 2000)
3242      * @type Number
3243      */
3244     this.maxSize = 2000;
3245     
3246     /**
3247      * Whether to animate the transition to the new size
3248      * @type Boolean
3249      */
3250     this.animate = false;
3251     
3252     /**
3253      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3254      * @type Boolean
3255      */
3256     this.useShim = false;
3257     
3258     /** @private */
3259     this.shim = null;
3260     
3261     if(!existingProxy){
3262         /** @private */
3263         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3264     }else{
3265         this.proxy = Roo.get(existingProxy).dom;
3266     }
3267     /** @private */
3268     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3269     
3270     /** @private */
3271     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3272     
3273     /** @private */
3274     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3275     
3276     /** @private */
3277     this.dragSpecs = {};
3278     
3279     /**
3280      * @private The adapter to use to positon and resize elements
3281      */
3282     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3283     this.adapter.init(this);
3284     
3285     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3286         /** @private */
3287         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3288         this.el.addClass("x-splitbar-h");
3289     }else{
3290         /** @private */
3291         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3292         this.el.addClass("x-splitbar-v");
3293     }
3294     
3295     this.addEvents({
3296         /**
3297          * @event resize
3298          * Fires when the splitter is moved (alias for {@link #event-moved})
3299          * @param {Roo.SplitBar} this
3300          * @param {Number} newSize the new width or height
3301          */
3302         "resize" : true,
3303         /**
3304          * @event moved
3305          * Fires when the splitter is moved
3306          * @param {Roo.SplitBar} this
3307          * @param {Number} newSize the new width or height
3308          */
3309         "moved" : true,
3310         /**
3311          * @event beforeresize
3312          * Fires before the splitter is dragged
3313          * @param {Roo.SplitBar} this
3314          */
3315         "beforeresize" : true,
3316
3317         "beforeapply" : true
3318     });
3319
3320     Roo.util.Observable.call(this);
3321 };
3322
3323 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3324     onStartProxyDrag : function(x, y){
3325         this.fireEvent("beforeresize", this);
3326         if(!this.overlay){
3327             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3328             o.unselectable();
3329             o.enableDisplayMode("block");
3330             // all splitbars share the same overlay
3331             Roo.SplitBar.prototype.overlay = o;
3332         }
3333         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3334         this.overlay.show();
3335         Roo.get(this.proxy).setDisplayed("block");
3336         var size = this.adapter.getElementSize(this);
3337         this.activeMinSize = this.getMinimumSize();;
3338         this.activeMaxSize = this.getMaximumSize();;
3339         var c1 = size - this.activeMinSize;
3340         var c2 = Math.max(this.activeMaxSize - size, 0);
3341         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3342             this.dd.resetConstraints();
3343             this.dd.setXConstraint(
3344                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3345                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3346             );
3347             this.dd.setYConstraint(0, 0);
3348         }else{
3349             this.dd.resetConstraints();
3350             this.dd.setXConstraint(0, 0);
3351             this.dd.setYConstraint(
3352                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3353                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3354             );
3355          }
3356         this.dragSpecs.startSize = size;
3357         this.dragSpecs.startPoint = [x, y];
3358         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3359     },
3360     
3361     /** 
3362      * @private Called after the drag operation by the DDProxy
3363      */
3364     onEndProxyDrag : function(e){
3365         Roo.get(this.proxy).setDisplayed(false);
3366         var endPoint = Roo.lib.Event.getXY(e);
3367         if(this.overlay){
3368             this.overlay.hide();
3369         }
3370         var newSize;
3371         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3372             newSize = this.dragSpecs.startSize + 
3373                 (this.placement == Roo.SplitBar.LEFT ?
3374                     endPoint[0] - this.dragSpecs.startPoint[0] :
3375                     this.dragSpecs.startPoint[0] - endPoint[0]
3376                 );
3377         }else{
3378             newSize = this.dragSpecs.startSize + 
3379                 (this.placement == Roo.SplitBar.TOP ?
3380                     endPoint[1] - this.dragSpecs.startPoint[1] :
3381                     this.dragSpecs.startPoint[1] - endPoint[1]
3382                 );
3383         }
3384         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3385         if(newSize != this.dragSpecs.startSize){
3386             if(this.fireEvent('beforeapply', this, newSize) !== false){
3387                 this.adapter.setElementSize(this, newSize);
3388                 this.fireEvent("moved", this, newSize);
3389                 this.fireEvent("resize", this, newSize);
3390             }
3391         }
3392     },
3393     
3394     /**
3395      * Get the adapter this SplitBar uses
3396      * @return The adapter object
3397      */
3398     getAdapter : function(){
3399         return this.adapter;
3400     },
3401     
3402     /**
3403      * Set the adapter this SplitBar uses
3404      * @param {Object} adapter A SplitBar adapter object
3405      */
3406     setAdapter : function(adapter){
3407         this.adapter = adapter;
3408         this.adapter.init(this);
3409     },
3410     
3411     /**
3412      * Gets the minimum size for the resizing element
3413      * @return {Number} The minimum size
3414      */
3415     getMinimumSize : function(){
3416         return this.minSize;
3417     },
3418     
3419     /**
3420      * Sets the minimum size for the resizing element
3421      * @param {Number} minSize The minimum size
3422      */
3423     setMinimumSize : function(minSize){
3424         this.minSize = minSize;
3425     },
3426     
3427     /**
3428      * Gets the maximum size for the resizing element
3429      * @return {Number} The maximum size
3430      */
3431     getMaximumSize : function(){
3432         return this.maxSize;
3433     },
3434     
3435     /**
3436      * Sets the maximum size for the resizing element
3437      * @param {Number} maxSize The maximum size
3438      */
3439     setMaximumSize : function(maxSize){
3440         this.maxSize = maxSize;
3441     },
3442     
3443     /**
3444      * Sets the initialize size for the resizing element
3445      * @param {Number} size The initial size
3446      */
3447     setCurrentSize : function(size){
3448         var oldAnimate = this.animate;
3449         this.animate = false;
3450         this.adapter.setElementSize(this, size);
3451         this.animate = oldAnimate;
3452     },
3453     
3454     /**
3455      * Destroy this splitbar. 
3456      * @param {Boolean} removeEl True to remove the element
3457      */
3458     destroy : function(removeEl){
3459         if(this.shim){
3460             this.shim.remove();
3461         }
3462         this.dd.unreg();
3463         this.proxy.parentNode.removeChild(this.proxy);
3464         if(removeEl){
3465             this.el.remove();
3466         }
3467     }
3468 });
3469
3470 /**
3471  * @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.
3472  */
3473 Roo.SplitBar.createProxy = function(dir){
3474     var proxy = new Roo.Element(document.createElement("div"));
3475     proxy.unselectable();
3476     var cls = 'x-splitbar-proxy';
3477     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3478     document.body.appendChild(proxy.dom);
3479     return proxy.dom;
3480 };
3481
3482 /** 
3483  * @class Roo.SplitBar.BasicLayoutAdapter
3484  * Default Adapter. It assumes the splitter and resizing element are not positioned
3485  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3486  */
3487 Roo.SplitBar.BasicLayoutAdapter = function(){
3488 };
3489
3490 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3491     // do nothing for now
3492     init : function(s){
3493     
3494     },
3495     /**
3496      * Called before drag operations to get the current size of the resizing element. 
3497      * @param {Roo.SplitBar} s The SplitBar using this adapter
3498      */
3499      getElementSize : function(s){
3500         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3501             return s.resizingEl.getWidth();
3502         }else{
3503             return s.resizingEl.getHeight();
3504         }
3505     },
3506     
3507     /**
3508      * Called after drag operations to set the size of the resizing element.
3509      * @param {Roo.SplitBar} s The SplitBar using this adapter
3510      * @param {Number} newSize The new size to set
3511      * @param {Function} onComplete A function to be invoked when resizing is complete
3512      */
3513     setElementSize : function(s, newSize, onComplete){
3514         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3515             if(!s.animate){
3516                 s.resizingEl.setWidth(newSize);
3517                 if(onComplete){
3518                     onComplete(s, newSize);
3519                 }
3520             }else{
3521                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3522             }
3523         }else{
3524             
3525             if(!s.animate){
3526                 s.resizingEl.setHeight(newSize);
3527                 if(onComplete){
3528                     onComplete(s, newSize);
3529                 }
3530             }else{
3531                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3532             }
3533         }
3534     }
3535 };
3536
3537 /** 
3538  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3539  * @extends Roo.SplitBar.BasicLayoutAdapter
3540  * Adapter that  moves the splitter element to align with the resized sizing element. 
3541  * Used with an absolute positioned SplitBar.
3542  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3543  * document.body, make sure you assign an id to the body element.
3544  */
3545 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3546     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3547     this.container = Roo.get(container);
3548 };
3549
3550 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3551     init : function(s){
3552         this.basic.init(s);
3553     },
3554     
3555     getElementSize : function(s){
3556         return this.basic.getElementSize(s);
3557     },
3558     
3559     setElementSize : function(s, newSize, onComplete){
3560         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3561     },
3562     
3563     moveSplitter : function(s){
3564         var yes = Roo.SplitBar;
3565         switch(s.placement){
3566             case yes.LEFT:
3567                 s.el.setX(s.resizingEl.getRight());
3568                 break;
3569             case yes.RIGHT:
3570                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3571                 break;
3572             case yes.TOP:
3573                 s.el.setY(s.resizingEl.getBottom());
3574                 break;
3575             case yes.BOTTOM:
3576                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3577                 break;
3578         }
3579     }
3580 };
3581
3582 /**
3583  * Orientation constant - Create a vertical SplitBar
3584  * @static
3585  * @type Number
3586  */
3587 Roo.SplitBar.VERTICAL = 1;
3588
3589 /**
3590  * Orientation constant - Create a horizontal SplitBar
3591  * @static
3592  * @type Number
3593  */
3594 Roo.SplitBar.HORIZONTAL = 2;
3595
3596 /**
3597  * Placement constant - The resizing element is to the left of the splitter element
3598  * @static
3599  * @type Number
3600  */
3601 Roo.SplitBar.LEFT = 1;
3602
3603 /**
3604  * Placement constant - The resizing element is to the right of the splitter element
3605  * @static
3606  * @type Number
3607  */
3608 Roo.SplitBar.RIGHT = 2;
3609
3610 /**
3611  * Placement constant - The resizing element is positioned above the splitter element
3612  * @static
3613  * @type Number
3614  */
3615 Roo.SplitBar.TOP = 3;
3616
3617 /**
3618  * Placement constant - The resizing element is positioned under splitter element
3619  * @static
3620  * @type Number
3621  */
3622 Roo.SplitBar.BOTTOM = 4;
3623 /*
3624  * Based on:
3625  * Ext JS Library 1.1.1
3626  * Copyright(c) 2006-2007, Ext JS, LLC.
3627  *
3628  * Originally Released Under LGPL - original licence link has changed is not relivant.
3629  *
3630  * Fork - LGPL
3631  * <script type="text/javascript">
3632  */
3633
3634 /**
3635  * @class Roo.View
3636  * @extends Roo.util.Observable
3637  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
3638  * This class also supports single and multi selection modes. <br>
3639  * Create a data model bound view:
3640  <pre><code>
3641  var store = new Roo.data.Store(...);
3642
3643  var view = new Roo.View({
3644     el : "my-element",
3645     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
3646  
3647     singleSelect: true,
3648     selectedClass: "ydataview-selected",
3649     store: store
3650  });
3651
3652  // listen for node click?
3653  view.on("click", function(vw, index, node, e){
3654  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
3655  });
3656
3657  // load XML data
3658  dataModel.load("foobar.xml");
3659  </code></pre>
3660  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
3661  * <br><br>
3662  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
3663  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
3664  * 
3665  * Note: old style constructor is still suported (container, template, config)
3666  * 
3667  * @constructor
3668  * Create a new View
3669  * @param {Object} config The config object
3670  * 
3671  */
3672 Roo.View = function(config, depreciated_tpl, depreciated_config){
3673     
3674     this.parent = false;
3675     
3676     if (typeof(depreciated_tpl) == 'undefined') {
3677         // new way.. - universal constructor.
3678         Roo.apply(this, config);
3679         this.el  = Roo.get(this.el);
3680     } else {
3681         // old format..
3682         this.el  = Roo.get(config);
3683         this.tpl = depreciated_tpl;
3684         Roo.apply(this, depreciated_config);
3685     }
3686     this.wrapEl  = this.el.wrap().wrap();
3687     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
3688     
3689     
3690     if(typeof(this.tpl) == "string"){
3691         this.tpl = new Roo.Template(this.tpl);
3692     } else {
3693         // support xtype ctors..
3694         this.tpl = new Roo.factory(this.tpl, Roo);
3695     }
3696     
3697     
3698     this.tpl.compile();
3699     
3700     /** @private */
3701     this.addEvents({
3702         /**
3703          * @event beforeclick
3704          * Fires before a click is processed. Returns false to cancel the default action.
3705          * @param {Roo.View} this
3706          * @param {Number} index The index of the target node
3707          * @param {HTMLElement} node The target node
3708          * @param {Roo.EventObject} e The raw event object
3709          */
3710             "beforeclick" : true,
3711         /**
3712          * @event click
3713          * Fires when a template node is clicked.
3714          * @param {Roo.View} this
3715          * @param {Number} index The index of the target node
3716          * @param {HTMLElement} node The target node
3717          * @param {Roo.EventObject} e The raw event object
3718          */
3719             "click" : true,
3720         /**
3721          * @event dblclick
3722          * Fires when a template node is double clicked.
3723          * @param {Roo.View} this
3724          * @param {Number} index The index of the target node
3725          * @param {HTMLElement} node The target node
3726          * @param {Roo.EventObject} e The raw event object
3727          */
3728             "dblclick" : true,
3729         /**
3730          * @event contextmenu
3731          * Fires when a template node is right clicked.
3732          * @param {Roo.View} this
3733          * @param {Number} index The index of the target node
3734          * @param {HTMLElement} node The target node
3735          * @param {Roo.EventObject} e The raw event object
3736          */
3737             "contextmenu" : true,
3738         /**
3739          * @event selectionchange
3740          * Fires when the selected nodes change.
3741          * @param {Roo.View} this
3742          * @param {Array} selections Array of the selected nodes
3743          */
3744             "selectionchange" : true,
3745     
3746         /**
3747          * @event beforeselect
3748          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
3749          * @param {Roo.View} this
3750          * @param {HTMLElement} node The node to be selected
3751          * @param {Array} selections Array of currently selected nodes
3752          */
3753             "beforeselect" : true,
3754         /**
3755          * @event preparedata
3756          * Fires on every row to render, to allow you to change the data.
3757          * @param {Roo.View} this
3758          * @param {Object} data to be rendered (change this)
3759          */
3760           "preparedata" : true
3761           
3762           
3763         });
3764
3765
3766
3767     this.el.on({
3768         "click": this.onClick,
3769         "dblclick": this.onDblClick,
3770         "contextmenu": this.onContextMenu,
3771         scope:this
3772     });
3773
3774     this.selections = [];
3775     this.nodes = [];
3776     this.cmp = new Roo.CompositeElementLite([]);
3777     if(this.store){
3778         this.store = Roo.factory(this.store, Roo.data);
3779         this.setStore(this.store, true);
3780     }
3781     
3782     if ( this.footer && this.footer.xtype) {
3783            
3784          var fctr = this.wrapEl.appendChild(document.createElement("div"));
3785         
3786         this.footer.dataSource = this.store;
3787         this.footer.container = fctr;
3788         this.footer = Roo.factory(this.footer, Roo);
3789         fctr.insertFirst(this.el);
3790         
3791         // this is a bit insane - as the paging toolbar seems to detach the el..
3792 //        dom.parentNode.parentNode.parentNode
3793          // they get detached?
3794     }
3795     
3796     
3797     Roo.View.superclass.constructor.call(this);
3798     
3799     
3800 };
3801
3802 Roo.extend(Roo.View, Roo.util.Observable, {
3803     
3804      /**
3805      * @cfg {Roo.data.Store} store Data store to load data from.
3806      */
3807     store : false,
3808     
3809     /**
3810      * @cfg {String|Roo.Element} el The container element.
3811      */
3812     el : '',
3813     
3814     /**
3815      * @cfg {String|Roo.Template} tpl The template used by this View 
3816      */
3817     tpl : false,
3818     /**
3819      * @cfg {String} dataName the named area of the template to use as the data area
3820      *                          Works with domtemplates roo-name="name"
3821      */
3822     dataName: false,
3823     /**
3824      * @cfg {String} selectedClass The css class to add to selected nodes
3825      */
3826     selectedClass : "x-view-selected",
3827      /**
3828      * @cfg {String} emptyText The empty text to show when nothing is loaded.
3829      */
3830     emptyText : "",
3831     
3832     /**
3833      * @cfg {String} text to display on mask (default Loading)
3834      */
3835     mask : false,
3836     /**
3837      * @cfg {Boolean} multiSelect Allow multiple selection
3838      */
3839     multiSelect : false,
3840     /**
3841      * @cfg {Boolean} singleSelect Allow single selection
3842      */
3843     singleSelect:  false,
3844     
3845     /**
3846      * @cfg {Boolean} toggleSelect - selecting 
3847      */
3848     toggleSelect : false,
3849     
3850     /**
3851      * @cfg {Boolean} tickable - selecting 
3852      */
3853     tickable : false,
3854     
3855     /**
3856      * Returns the element this view is bound to.
3857      * @return {Roo.Element}
3858      */
3859     getEl : function(){
3860         return this.wrapEl;
3861     },
3862     
3863     
3864
3865     /**
3866      * Refreshes the view. - called by datachanged on the store. - do not call directly.
3867      */
3868     refresh : function(){
3869         //Roo.log('refresh');
3870         var t = this.tpl;
3871         
3872         // if we are using something like 'domtemplate', then
3873         // the what gets used is:
3874         // t.applySubtemplate(NAME, data, wrapping data..)
3875         // the outer template then get' applied with
3876         //     the store 'extra data'
3877         // and the body get's added to the
3878         //      roo-name="data" node?
3879         //      <span class='roo-tpl-{name}'></span> ?????
3880         
3881         
3882         
3883         this.clearSelections();
3884         this.el.update("");
3885         var html = [];
3886         var records = this.store.getRange();
3887         if(records.length < 1) {
3888             
3889             // is this valid??  = should it render a template??
3890             
3891             this.el.update(this.emptyText);
3892             return;
3893         }
3894         var el = this.el;
3895         if (this.dataName) {
3896             this.el.update(t.apply(this.store.meta)); //????
3897             el = this.el.child('.roo-tpl-' + this.dataName);
3898         }
3899         
3900         for(var i = 0, len = records.length; i < len; i++){
3901             var data = this.prepareData(records[i].data, i, records[i]);
3902             this.fireEvent("preparedata", this, data, i, records[i]);
3903             
3904             var d = Roo.apply({}, data);
3905             
3906             if(this.tickable){
3907                 Roo.apply(d, {'roo-id' : Roo.id()});
3908                 
3909                 var _this = this;
3910             
3911                 Roo.each(this.parent.item, function(item){
3912                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
3913                         return;
3914                     }
3915                     Roo.apply(d, {'roo-data-checked' : 'checked'});
3916                 });
3917             }
3918             
3919             html[html.length] = Roo.util.Format.trim(
3920                 this.dataName ?
3921                     t.applySubtemplate(this.dataName, d, this.store.meta) :
3922                     t.apply(d)
3923             );
3924         }
3925         
3926         
3927         
3928         el.update(html.join(""));
3929         this.nodes = el.dom.childNodes;
3930         this.updateIndexes(0);
3931     },
3932     
3933
3934     /**
3935      * Function to override to reformat the data that is sent to
3936      * the template for each node.
3937      * DEPRICATED - use the preparedata event handler.
3938      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
3939      * a JSON object for an UpdateManager bound view).
3940      */
3941     prepareData : function(data, index, record)
3942     {
3943         this.fireEvent("preparedata", this, data, index, record);
3944         return data;
3945     },
3946
3947     onUpdate : function(ds, record){
3948         // Roo.log('on update');   
3949         this.clearSelections();
3950         var index = this.store.indexOf(record);
3951         var n = this.nodes[index];
3952         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
3953         n.parentNode.removeChild(n);
3954         this.updateIndexes(index, index);
3955     },
3956
3957     
3958     
3959 // --------- FIXME     
3960     onAdd : function(ds, records, index)
3961     {
3962         //Roo.log(['on Add', ds, records, index] );        
3963         this.clearSelections();
3964         if(this.nodes.length == 0){
3965             this.refresh();
3966             return;
3967         }
3968         var n = this.nodes[index];
3969         for(var i = 0, len = records.length; i < len; i++){
3970             var d = this.prepareData(records[i].data, i, records[i]);
3971             if(n){
3972                 this.tpl.insertBefore(n, d);
3973             }else{
3974                 
3975                 this.tpl.append(this.el, d);
3976             }
3977         }
3978         this.updateIndexes(index);
3979     },
3980
3981     onRemove : function(ds, record, index){
3982        // Roo.log('onRemove');
3983         this.clearSelections();
3984         var el = this.dataName  ?
3985             this.el.child('.roo-tpl-' + this.dataName) :
3986             this.el; 
3987         
3988         el.dom.removeChild(this.nodes[index]);
3989         this.updateIndexes(index);
3990     },
3991
3992     /**
3993      * Refresh an individual node.
3994      * @param {Number} index
3995      */
3996     refreshNode : function(index){
3997         this.onUpdate(this.store, this.store.getAt(index));
3998     },
3999
4000     updateIndexes : function(startIndex, endIndex){
4001         var ns = this.nodes;
4002         startIndex = startIndex || 0;
4003         endIndex = endIndex || ns.length - 1;
4004         for(var i = startIndex; i <= endIndex; i++){
4005             ns[i].nodeIndex = i;
4006         }
4007     },
4008
4009     /**
4010      * Changes the data store this view uses and refresh the view.
4011      * @param {Store} store
4012      */
4013     setStore : function(store, initial){
4014         if(!initial && this.store){
4015             this.store.un("datachanged", this.refresh);
4016             this.store.un("add", this.onAdd);
4017             this.store.un("remove", this.onRemove);
4018             this.store.un("update", this.onUpdate);
4019             this.store.un("clear", this.refresh);
4020             this.store.un("beforeload", this.onBeforeLoad);
4021             this.store.un("load", this.onLoad);
4022             this.store.un("loadexception", this.onLoad);
4023         }
4024         if(store){
4025           
4026             store.on("datachanged", this.refresh, this);
4027             store.on("add", this.onAdd, this);
4028             store.on("remove", this.onRemove, this);
4029             store.on("update", this.onUpdate, this);
4030             store.on("clear", this.refresh, this);
4031             store.on("beforeload", this.onBeforeLoad, this);
4032             store.on("load", this.onLoad, this);
4033             store.on("loadexception", this.onLoad, this);
4034         }
4035         
4036         if(store){
4037             this.refresh();
4038         }
4039     },
4040     /**
4041      * onbeforeLoad - masks the loading area.
4042      *
4043      */
4044     onBeforeLoad : function(store,opts)
4045     {
4046          //Roo.log('onBeforeLoad');   
4047         if (!opts.add) {
4048             this.el.update("");
4049         }
4050         this.el.mask(this.mask ? this.mask : "Loading" ); 
4051     },
4052     onLoad : function ()
4053     {
4054         this.el.unmask();
4055     },
4056     
4057
4058     /**
4059      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4060      * @param {HTMLElement} node
4061      * @return {HTMLElement} The template node
4062      */
4063     findItemFromChild : function(node){
4064         var el = this.dataName  ?
4065             this.el.child('.roo-tpl-' + this.dataName,true) :
4066             this.el.dom; 
4067         
4068         if(!node || node.parentNode == el){
4069                     return node;
4070             }
4071             var p = node.parentNode;
4072             while(p && p != el){
4073             if(p.parentNode == el){
4074                 return p;
4075             }
4076             p = p.parentNode;
4077         }
4078             return null;
4079     },
4080
4081     /** @ignore */
4082     onClick : function(e){
4083         var item = this.findItemFromChild(e.getTarget());
4084         if(item){
4085             var index = this.indexOf(item);
4086             if(this.onItemClick(item, index, e) !== false){
4087                 this.fireEvent("click", this, index, item, e);
4088             }
4089         }else{
4090             this.clearSelections();
4091         }
4092     },
4093
4094     /** @ignore */
4095     onContextMenu : function(e){
4096         var item = this.findItemFromChild(e.getTarget());
4097         if(item){
4098             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4099         }
4100     },
4101
4102     /** @ignore */
4103     onDblClick : function(e){
4104         var item = this.findItemFromChild(e.getTarget());
4105         if(item){
4106             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4107         }
4108     },
4109
4110     onItemClick : function(item, index, e)
4111     {
4112         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4113             return false;
4114         }
4115         if (this.toggleSelect) {
4116             var m = this.isSelected(item) ? 'unselect' : 'select';
4117             //Roo.log(m);
4118             var _t = this;
4119             _t[m](item, true, false);
4120             return true;
4121         }
4122         if(this.multiSelect || this.singleSelect){
4123             if(this.multiSelect && e.shiftKey && this.lastSelection){
4124                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4125             }else{
4126                 this.select(item, this.multiSelect && e.ctrlKey);
4127                 this.lastSelection = item;
4128             }
4129             
4130             if(!this.tickable){
4131                 e.preventDefault();
4132             }
4133             
4134         }
4135         return true;
4136     },
4137
4138     /**
4139      * Get the number of selected nodes.
4140      * @return {Number}
4141      */
4142     getSelectionCount : function(){
4143         return this.selections.length;
4144     },
4145
4146     /**
4147      * Get the currently selected nodes.
4148      * @return {Array} An array of HTMLElements
4149      */
4150     getSelectedNodes : function(){
4151         return this.selections;
4152     },
4153
4154     /**
4155      * Get the indexes of the selected nodes.
4156      * @return {Array}
4157      */
4158     getSelectedIndexes : function(){
4159         var indexes = [], s = this.selections;
4160         for(var i = 0, len = s.length; i < len; i++){
4161             indexes.push(s[i].nodeIndex);
4162         }
4163         return indexes;
4164     },
4165
4166     /**
4167      * Clear all selections
4168      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4169      */
4170     clearSelections : function(suppressEvent){
4171         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4172             this.cmp.elements = this.selections;
4173             this.cmp.removeClass(this.selectedClass);
4174             this.selections = [];
4175             if(!suppressEvent){
4176                 this.fireEvent("selectionchange", this, this.selections);
4177             }
4178         }
4179     },
4180
4181     /**
4182      * Returns true if the passed node is selected
4183      * @param {HTMLElement/Number} node The node or node index
4184      * @return {Boolean}
4185      */
4186     isSelected : function(node){
4187         var s = this.selections;
4188         if(s.length < 1){
4189             return false;
4190         }
4191         node = this.getNode(node);
4192         return s.indexOf(node) !== -1;
4193     },
4194
4195     /**
4196      * Selects nodes.
4197      * @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
4198      * @param {Boolean} keepExisting (optional) true to keep existing selections
4199      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4200      */
4201     select : function(nodeInfo, keepExisting, suppressEvent){
4202         if(nodeInfo instanceof Array){
4203             if(!keepExisting){
4204                 this.clearSelections(true);
4205             }
4206             for(var i = 0, len = nodeInfo.length; i < len; i++){
4207                 this.select(nodeInfo[i], true, true);
4208             }
4209             return;
4210         } 
4211         var node = this.getNode(nodeInfo);
4212         if(!node || this.isSelected(node)){
4213             return; // already selected.
4214         }
4215         if(!keepExisting){
4216             this.clearSelections(true);
4217         }
4218         
4219         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4220             Roo.fly(node).addClass(this.selectedClass);
4221             this.selections.push(node);
4222             if(!suppressEvent){
4223                 this.fireEvent("selectionchange", this, this.selections);
4224             }
4225         }
4226         
4227         
4228     },
4229       /**
4230      * Unselects nodes.
4231      * @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
4232      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4233      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4234      */
4235     unselect : function(nodeInfo, keepExisting, suppressEvent)
4236     {
4237         if(nodeInfo instanceof Array){
4238             Roo.each(this.selections, function(s) {
4239                 this.unselect(s, nodeInfo);
4240             }, this);
4241             return;
4242         }
4243         var node = this.getNode(nodeInfo);
4244         if(!node || !this.isSelected(node)){
4245             //Roo.log("not selected");
4246             return; // not selected.
4247         }
4248         // fireevent???
4249         var ns = [];
4250         Roo.each(this.selections, function(s) {
4251             if (s == node ) {
4252                 Roo.fly(node).removeClass(this.selectedClass);
4253
4254                 return;
4255             }
4256             ns.push(s);
4257         },this);
4258         
4259         this.selections= ns;
4260         this.fireEvent("selectionchange", this, this.selections);
4261     },
4262
4263     /**
4264      * Gets a template node.
4265      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4266      * @return {HTMLElement} The node or null if it wasn't found
4267      */
4268     getNode : function(nodeInfo){
4269         if(typeof nodeInfo == "string"){
4270             return document.getElementById(nodeInfo);
4271         }else if(typeof nodeInfo == "number"){
4272             return this.nodes[nodeInfo];
4273         }
4274         return nodeInfo;
4275     },
4276
4277     /**
4278      * Gets a range template nodes.
4279      * @param {Number} startIndex
4280      * @param {Number} endIndex
4281      * @return {Array} An array of nodes
4282      */
4283     getNodes : function(start, end){
4284         var ns = this.nodes;
4285         start = start || 0;
4286         end = typeof end == "undefined" ? ns.length - 1 : end;
4287         var nodes = [];
4288         if(start <= end){
4289             for(var i = start; i <= end; i++){
4290                 nodes.push(ns[i]);
4291             }
4292         } else{
4293             for(var i = start; i >= end; i--){
4294                 nodes.push(ns[i]);
4295             }
4296         }
4297         return nodes;
4298     },
4299
4300     /**
4301      * Finds the index of the passed node
4302      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4303      * @return {Number} The index of the node or -1
4304      */
4305     indexOf : function(node){
4306         node = this.getNode(node);
4307         if(typeof node.nodeIndex == "number"){
4308             return node.nodeIndex;
4309         }
4310         var ns = this.nodes;
4311         for(var i = 0, len = ns.length; i < len; i++){
4312             if(ns[i] == node){
4313                 return i;
4314             }
4315         }
4316         return -1;
4317     }
4318 });
4319 /*
4320  * Based on:
4321  * Ext JS Library 1.1.1
4322  * Copyright(c) 2006-2007, Ext JS, LLC.
4323  *
4324  * Originally Released Under LGPL - original licence link has changed is not relivant.
4325  *
4326  * Fork - LGPL
4327  * <script type="text/javascript">
4328  */
4329
4330 /**
4331  * @class Roo.JsonView
4332  * @extends Roo.View
4333  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4334 <pre><code>
4335 var view = new Roo.JsonView({
4336     container: "my-element",
4337     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4338     multiSelect: true, 
4339     jsonRoot: "data" 
4340 });
4341
4342 // listen for node click?
4343 view.on("click", function(vw, index, node, e){
4344     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4345 });
4346
4347 // direct load of JSON data
4348 view.load("foobar.php");
4349
4350 // Example from my blog list
4351 var tpl = new Roo.Template(
4352     '&lt;div class="entry"&gt;' +
4353     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4354     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4355     "&lt;/div&gt;&lt;hr /&gt;"
4356 );
4357
4358 var moreView = new Roo.JsonView({
4359     container :  "entry-list", 
4360     template : tpl,
4361     jsonRoot: "posts"
4362 });
4363 moreView.on("beforerender", this.sortEntries, this);
4364 moreView.load({
4365     url: "/blog/get-posts.php",
4366     params: "allposts=true",
4367     text: "Loading Blog Entries..."
4368 });
4369 </code></pre>
4370
4371 * Note: old code is supported with arguments : (container, template, config)
4372
4373
4374  * @constructor
4375  * Create a new JsonView
4376  * 
4377  * @param {Object} config The config object
4378  * 
4379  */
4380 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4381     
4382     
4383     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4384
4385     var um = this.el.getUpdateManager();
4386     um.setRenderer(this);
4387     um.on("update", this.onLoad, this);
4388     um.on("failure", this.onLoadException, this);
4389
4390     /**
4391      * @event beforerender
4392      * Fires before rendering of the downloaded JSON data.
4393      * @param {Roo.JsonView} this
4394      * @param {Object} data The JSON data loaded
4395      */
4396     /**
4397      * @event load
4398      * Fires when data is loaded.
4399      * @param {Roo.JsonView} this
4400      * @param {Object} data The JSON data loaded
4401      * @param {Object} response The raw Connect response object
4402      */
4403     /**
4404      * @event loadexception
4405      * Fires when loading fails.
4406      * @param {Roo.JsonView} this
4407      * @param {Object} response The raw Connect response object
4408      */
4409     this.addEvents({
4410         'beforerender' : true,
4411         'load' : true,
4412         'loadexception' : true
4413     });
4414 };
4415 Roo.extend(Roo.JsonView, Roo.View, {
4416     /**
4417      * @type {String} The root property in the loaded JSON object that contains the data
4418      */
4419     jsonRoot : "",
4420
4421     /**
4422      * Refreshes the view.
4423      */
4424     refresh : function(){
4425         this.clearSelections();
4426         this.el.update("");
4427         var html = [];
4428         var o = this.jsonData;
4429         if(o && o.length > 0){
4430             for(var i = 0, len = o.length; i < len; i++){
4431                 var data = this.prepareData(o[i], i, o);
4432                 html[html.length] = this.tpl.apply(data);
4433             }
4434         }else{
4435             html.push(this.emptyText);
4436         }
4437         this.el.update(html.join(""));
4438         this.nodes = this.el.dom.childNodes;
4439         this.updateIndexes(0);
4440     },
4441
4442     /**
4443      * 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.
4444      * @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:
4445      <pre><code>
4446      view.load({
4447          url: "your-url.php",
4448          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4449          callback: yourFunction,
4450          scope: yourObject, //(optional scope)
4451          discardUrl: false,
4452          nocache: false,
4453          text: "Loading...",
4454          timeout: 30,
4455          scripts: false
4456      });
4457      </code></pre>
4458      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4459      * 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.
4460      * @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}
4461      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4462      * @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.
4463      */
4464     load : function(){
4465         var um = this.el.getUpdateManager();
4466         um.update.apply(um, arguments);
4467     },
4468
4469     // note - render is a standard framework call...
4470     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4471     render : function(el, response){
4472         
4473         this.clearSelections();
4474         this.el.update("");
4475         var o;
4476         try{
4477             if (response != '') {
4478                 o = Roo.util.JSON.decode(response.responseText);
4479                 if(this.jsonRoot){
4480                     
4481                     o = o[this.jsonRoot];
4482                 }
4483             }
4484         } catch(e){
4485         }
4486         /**
4487          * The current JSON data or null
4488          */
4489         this.jsonData = o;
4490         this.beforeRender();
4491         this.refresh();
4492     },
4493
4494 /**
4495  * Get the number of records in the current JSON dataset
4496  * @return {Number}
4497  */
4498     getCount : function(){
4499         return this.jsonData ? this.jsonData.length : 0;
4500     },
4501
4502 /**
4503  * Returns the JSON object for the specified node(s)
4504  * @param {HTMLElement/Array} node The node or an array of nodes
4505  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4506  * you get the JSON object for the node
4507  */
4508     getNodeData : function(node){
4509         if(node instanceof Array){
4510             var data = [];
4511             for(var i = 0, len = node.length; i < len; i++){
4512                 data.push(this.getNodeData(node[i]));
4513             }
4514             return data;
4515         }
4516         return this.jsonData[this.indexOf(node)] || null;
4517     },
4518
4519     beforeRender : function(){
4520         this.snapshot = this.jsonData;
4521         if(this.sortInfo){
4522             this.sort.apply(this, this.sortInfo);
4523         }
4524         this.fireEvent("beforerender", this, this.jsonData);
4525     },
4526
4527     onLoad : function(el, o){
4528         this.fireEvent("load", this, this.jsonData, o);
4529     },
4530
4531     onLoadException : function(el, o){
4532         this.fireEvent("loadexception", this, o);
4533     },
4534
4535 /**
4536  * Filter the data by a specific property.
4537  * @param {String} property A property on your JSON objects
4538  * @param {String/RegExp} value Either string that the property values
4539  * should start with, or a RegExp to test against the property
4540  */
4541     filter : function(property, value){
4542         if(this.jsonData){
4543             var data = [];
4544             var ss = this.snapshot;
4545             if(typeof value == "string"){
4546                 var vlen = value.length;
4547                 if(vlen == 0){
4548                     this.clearFilter();
4549                     return;
4550                 }
4551                 value = value.toLowerCase();
4552                 for(var i = 0, len = ss.length; i < len; i++){
4553                     var o = ss[i];
4554                     if(o[property].substr(0, vlen).toLowerCase() == value){
4555                         data.push(o);
4556                     }
4557                 }
4558             } else if(value.exec){ // regex?
4559                 for(var i = 0, len = ss.length; i < len; i++){
4560                     var o = ss[i];
4561                     if(value.test(o[property])){
4562                         data.push(o);
4563                     }
4564                 }
4565             } else{
4566                 return;
4567             }
4568             this.jsonData = data;
4569             this.refresh();
4570         }
4571     },
4572
4573 /**
4574  * Filter by a function. The passed function will be called with each
4575  * object in the current dataset. If the function returns true the value is kept,
4576  * otherwise it is filtered.
4577  * @param {Function} fn
4578  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4579  */
4580     filterBy : function(fn, scope){
4581         if(this.jsonData){
4582             var data = [];
4583             var ss = this.snapshot;
4584             for(var i = 0, len = ss.length; i < len; i++){
4585                 var o = ss[i];
4586                 if(fn.call(scope || this, o)){
4587                     data.push(o);
4588                 }
4589             }
4590             this.jsonData = data;
4591             this.refresh();
4592         }
4593     },
4594
4595 /**
4596  * Clears the current filter.
4597  */
4598     clearFilter : function(){
4599         if(this.snapshot && this.jsonData != this.snapshot){
4600             this.jsonData = this.snapshot;
4601             this.refresh();
4602         }
4603     },
4604
4605
4606 /**
4607  * Sorts the data for this view and refreshes it.
4608  * @param {String} property A property on your JSON objects to sort on
4609  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
4610  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
4611  */
4612     sort : function(property, dir, sortType){
4613         this.sortInfo = Array.prototype.slice.call(arguments, 0);
4614         if(this.jsonData){
4615             var p = property;
4616             var dsc = dir && dir.toLowerCase() == "desc";
4617             var f = function(o1, o2){
4618                 var v1 = sortType ? sortType(o1[p]) : o1[p];
4619                 var v2 = sortType ? sortType(o2[p]) : o2[p];
4620                 ;
4621                 if(v1 < v2){
4622                     return dsc ? +1 : -1;
4623                 } else if(v1 > v2){
4624                     return dsc ? -1 : +1;
4625                 } else{
4626                     return 0;
4627                 }
4628             };
4629             this.jsonData.sort(f);
4630             this.refresh();
4631             if(this.jsonData != this.snapshot){
4632                 this.snapshot.sort(f);
4633             }
4634         }
4635     }
4636 });/*
4637  * Based on:
4638  * Ext JS Library 1.1.1
4639  * Copyright(c) 2006-2007, Ext JS, LLC.
4640  *
4641  * Originally Released Under LGPL - original licence link has changed is not relivant.
4642  *
4643  * Fork - LGPL
4644  * <script type="text/javascript">
4645  */
4646  
4647
4648 /**
4649  * @class Roo.ColorPalette
4650  * @extends Roo.Component
4651  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
4652  * Here's an example of typical usage:
4653  * <pre><code>
4654 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
4655 cp.render('my-div');
4656
4657 cp.on('select', function(palette, selColor){
4658     // do something with selColor
4659 });
4660 </code></pre>
4661  * @constructor
4662  * Create a new ColorPalette
4663  * @param {Object} config The config object
4664  */
4665 Roo.ColorPalette = function(config){
4666     Roo.ColorPalette.superclass.constructor.call(this, config);
4667     this.addEvents({
4668         /**
4669              * @event select
4670              * Fires when a color is selected
4671              * @param {ColorPalette} this
4672              * @param {String} color The 6-digit color hex code (without the # symbol)
4673              */
4674         select: true
4675     });
4676
4677     if(this.handler){
4678         this.on("select", this.handler, this.scope, true);
4679     }
4680 };
4681 Roo.extend(Roo.ColorPalette, Roo.Component, {
4682     /**
4683      * @cfg {String} itemCls
4684      * The CSS class to apply to the containing element (defaults to "x-color-palette")
4685      */
4686     itemCls : "x-color-palette",
4687     /**
4688      * @cfg {String} value
4689      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
4690      * the hex codes are case-sensitive.
4691      */
4692     value : null,
4693     clickEvent:'click',
4694     // private
4695     ctype: "Roo.ColorPalette",
4696
4697     /**
4698      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
4699      */
4700     allowReselect : false,
4701
4702     /**
4703      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
4704      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
4705      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
4706      * of colors with the width setting until the box is symmetrical.</p>
4707      * <p>You can override individual colors if needed:</p>
4708      * <pre><code>
4709 var cp = new Roo.ColorPalette();
4710 cp.colors[0] = "FF0000";  // change the first box to red
4711 </code></pre>
4712
4713 Or you can provide a custom array of your own for complete control:
4714 <pre><code>
4715 var cp = new Roo.ColorPalette();
4716 cp.colors = ["000000", "993300", "333300"];
4717 </code></pre>
4718      * @type Array
4719      */
4720     colors : [
4721         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
4722         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
4723         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
4724         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
4725         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
4726     ],
4727
4728     // private
4729     onRender : function(container, position){
4730         var t = new Roo.MasterTemplate(
4731             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
4732         );
4733         var c = this.colors;
4734         for(var i = 0, len = c.length; i < len; i++){
4735             t.add([c[i]]);
4736         }
4737         var el = document.createElement("div");
4738         el.className = this.itemCls;
4739         t.overwrite(el);
4740         container.dom.insertBefore(el, position);
4741         this.el = Roo.get(el);
4742         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
4743         if(this.clickEvent != 'click'){
4744             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
4745         }
4746     },
4747
4748     // private
4749     afterRender : function(){
4750         Roo.ColorPalette.superclass.afterRender.call(this);
4751         if(this.value){
4752             var s = this.value;
4753             this.value = null;
4754             this.select(s);
4755         }
4756     },
4757
4758     // private
4759     handleClick : function(e, t){
4760         e.preventDefault();
4761         if(!this.disabled){
4762             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
4763             this.select(c.toUpperCase());
4764         }
4765     },
4766
4767     /**
4768      * Selects the specified color in the palette (fires the select event)
4769      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
4770      */
4771     select : function(color){
4772         color = color.replace("#", "");
4773         if(color != this.value || this.allowReselect){
4774             var el = this.el;
4775             if(this.value){
4776                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
4777             }
4778             el.child("a.color-"+color).addClass("x-color-palette-sel");
4779             this.value = color;
4780             this.fireEvent("select", this, color);
4781         }
4782     }
4783 });/*
4784  * Based on:
4785  * Ext JS Library 1.1.1
4786  * Copyright(c) 2006-2007, Ext JS, LLC.
4787  *
4788  * Originally Released Under LGPL - original licence link has changed is not relivant.
4789  *
4790  * Fork - LGPL
4791  * <script type="text/javascript">
4792  */
4793  
4794 /**
4795  * @class Roo.DatePicker
4796  * @extends Roo.Component
4797  * Simple date picker class.
4798  * @constructor
4799  * Create a new DatePicker
4800  * @param {Object} config The config object
4801  */
4802 Roo.DatePicker = function(config){
4803     Roo.DatePicker.superclass.constructor.call(this, config);
4804
4805     this.value = config && config.value ?
4806                  config.value.clearTime() : new Date().clearTime();
4807
4808     this.addEvents({
4809         /**
4810              * @event select
4811              * Fires when a date is selected
4812              * @param {DatePicker} this
4813              * @param {Date} date The selected date
4814              */
4815         'select': true,
4816         /**
4817              * @event monthchange
4818              * Fires when the displayed month changes 
4819              * @param {DatePicker} this
4820              * @param {Date} date The selected month
4821              */
4822         'monthchange': true
4823     });
4824
4825     if(this.handler){
4826         this.on("select", this.handler,  this.scope || this);
4827     }
4828     // build the disabledDatesRE
4829     if(!this.disabledDatesRE && this.disabledDates){
4830         var dd = this.disabledDates;
4831         var re = "(?:";
4832         for(var i = 0; i < dd.length; i++){
4833             re += dd[i];
4834             if(i != dd.length-1) {
4835                 re += "|";
4836             }
4837         }
4838         this.disabledDatesRE = new RegExp(re + ")");
4839     }
4840 };
4841
4842 Roo.extend(Roo.DatePicker, Roo.Component, {
4843     /**
4844      * @cfg {String} todayText
4845      * The text to display on the button that selects the current date (defaults to "Today")
4846      */
4847     todayText : "Today",
4848     /**
4849      * @cfg {String} okText
4850      * The text to display on the ok button
4851      */
4852     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
4853     /**
4854      * @cfg {String} cancelText
4855      * The text to display on the cancel button
4856      */
4857     cancelText : "Cancel",
4858     /**
4859      * @cfg {String} todayTip
4860      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
4861      */
4862     todayTip : "{0} (Spacebar)",
4863     /**
4864      * @cfg {Date} minDate
4865      * Minimum allowable date (JavaScript date object, defaults to null)
4866      */
4867     minDate : null,
4868     /**
4869      * @cfg {Date} maxDate
4870      * Maximum allowable date (JavaScript date object, defaults to null)
4871      */
4872     maxDate : null,
4873     /**
4874      * @cfg {String} minText
4875      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
4876      */
4877     minText : "This date is before the minimum date",
4878     /**
4879      * @cfg {String} maxText
4880      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
4881      */
4882     maxText : "This date is after the maximum date",
4883     /**
4884      * @cfg {String} format
4885      * The default date format string which can be overriden for localization support.  The format must be
4886      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
4887      */
4888     format : "m/d/y",
4889     /**
4890      * @cfg {Array} disabledDays
4891      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
4892      */
4893     disabledDays : null,
4894     /**
4895      * @cfg {String} disabledDaysText
4896      * The tooltip to display when the date falls on a disabled day (defaults to "")
4897      */
4898     disabledDaysText : "",
4899     /**
4900      * @cfg {RegExp} disabledDatesRE
4901      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
4902      */
4903     disabledDatesRE : null,
4904     /**
4905      * @cfg {String} disabledDatesText
4906      * The tooltip text to display when the date falls on a disabled date (defaults to "")
4907      */
4908     disabledDatesText : "",
4909     /**
4910      * @cfg {Boolean} constrainToViewport
4911      * True to constrain the date picker to the viewport (defaults to true)
4912      */
4913     constrainToViewport : true,
4914     /**
4915      * @cfg {Array} monthNames
4916      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
4917      */
4918     monthNames : Date.monthNames,
4919     /**
4920      * @cfg {Array} dayNames
4921      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
4922      */
4923     dayNames : Date.dayNames,
4924     /**
4925      * @cfg {String} nextText
4926      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
4927      */
4928     nextText: 'Next Month (Control+Right)',
4929     /**
4930      * @cfg {String} prevText
4931      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
4932      */
4933     prevText: 'Previous Month (Control+Left)',
4934     /**
4935      * @cfg {String} monthYearText
4936      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
4937      */
4938     monthYearText: 'Choose a month (Control+Up/Down to move years)',
4939     /**
4940      * @cfg {Number} startDay
4941      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
4942      */
4943     startDay : 0,
4944     /**
4945      * @cfg {Bool} showClear
4946      * Show a clear button (usefull for date form elements that can be blank.)
4947      */
4948     
4949     showClear: false,
4950     
4951     /**
4952      * Sets the value of the date field
4953      * @param {Date} value The date to set
4954      */
4955     setValue : function(value){
4956         var old = this.value;
4957         
4958         if (typeof(value) == 'string') {
4959          
4960             value = Date.parseDate(value, this.format);
4961         }
4962         if (!value) {
4963             value = new Date();
4964         }
4965         
4966         this.value = value.clearTime(true);
4967         if(this.el){
4968             this.update(this.value);
4969         }
4970     },
4971
4972     /**
4973      * Gets the current selected value of the date field
4974      * @return {Date} The selected date
4975      */
4976     getValue : function(){
4977         return this.value;
4978     },
4979
4980     // private
4981     focus : function(){
4982         if(this.el){
4983             this.update(this.activeDate);
4984         }
4985     },
4986
4987     // privateval
4988     onRender : function(container, position){
4989         
4990         var m = [
4991              '<table cellspacing="0">',
4992                 '<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>',
4993                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
4994         var dn = this.dayNames;
4995         for(var i = 0; i < 7; i++){
4996             var d = this.startDay+i;
4997             if(d > 6){
4998                 d = d-7;
4999             }
5000             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5001         }
5002         m[m.length] = "</tr></thead><tbody><tr>";
5003         for(var i = 0; i < 42; i++) {
5004             if(i % 7 == 0 && i != 0){
5005                 m[m.length] = "</tr><tr>";
5006             }
5007             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5008         }
5009         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5010             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5011
5012         var el = document.createElement("div");
5013         el.className = "x-date-picker";
5014         el.innerHTML = m.join("");
5015
5016         container.dom.insertBefore(el, position);
5017
5018         this.el = Roo.get(el);
5019         this.eventEl = Roo.get(el.firstChild);
5020
5021         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5022             handler: this.showPrevMonth,
5023             scope: this,
5024             preventDefault:true,
5025             stopDefault:true
5026         });
5027
5028         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5029             handler: this.showNextMonth,
5030             scope: this,
5031             preventDefault:true,
5032             stopDefault:true
5033         });
5034
5035         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5036
5037         this.monthPicker = this.el.down('div.x-date-mp');
5038         this.monthPicker.enableDisplayMode('block');
5039         
5040         var kn = new Roo.KeyNav(this.eventEl, {
5041             "left" : function(e){
5042                 e.ctrlKey ?
5043                     this.showPrevMonth() :
5044                     this.update(this.activeDate.add("d", -1));
5045             },
5046
5047             "right" : function(e){
5048                 e.ctrlKey ?
5049                     this.showNextMonth() :
5050                     this.update(this.activeDate.add("d", 1));
5051             },
5052
5053             "up" : function(e){
5054                 e.ctrlKey ?
5055                     this.showNextYear() :
5056                     this.update(this.activeDate.add("d", -7));
5057             },
5058
5059             "down" : function(e){
5060                 e.ctrlKey ?
5061                     this.showPrevYear() :
5062                     this.update(this.activeDate.add("d", 7));
5063             },
5064
5065             "pageUp" : function(e){
5066                 this.showNextMonth();
5067             },
5068
5069             "pageDown" : function(e){
5070                 this.showPrevMonth();
5071             },
5072
5073             "enter" : function(e){
5074                 e.stopPropagation();
5075                 return true;
5076             },
5077
5078             scope : this
5079         });
5080
5081         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5082
5083         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5084
5085         this.el.unselectable();
5086         
5087         this.cells = this.el.select("table.x-date-inner tbody td");
5088         this.textNodes = this.el.query("table.x-date-inner tbody span");
5089
5090         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5091             text: "&#160;",
5092             tooltip: this.monthYearText
5093         });
5094
5095         this.mbtn.on('click', this.showMonthPicker, this);
5096         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5097
5098
5099         var today = (new Date()).dateFormat(this.format);
5100         
5101         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5102         if (this.showClear) {
5103             baseTb.add( new Roo.Toolbar.Fill());
5104         }
5105         baseTb.add({
5106             text: String.format(this.todayText, today),
5107             tooltip: String.format(this.todayTip, today),
5108             handler: this.selectToday,
5109             scope: this
5110         });
5111         
5112         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5113             
5114         //});
5115         if (this.showClear) {
5116             
5117             baseTb.add( new Roo.Toolbar.Fill());
5118             baseTb.add({
5119                 text: '&#160;',
5120                 cls: 'x-btn-icon x-btn-clear',
5121                 handler: function() {
5122                     //this.value = '';
5123                     this.fireEvent("select", this, '');
5124                 },
5125                 scope: this
5126             });
5127         }
5128         
5129         
5130         if(Roo.isIE){
5131             this.el.repaint();
5132         }
5133         this.update(this.value);
5134     },
5135
5136     createMonthPicker : function(){
5137         if(!this.monthPicker.dom.firstChild){
5138             var buf = ['<table border="0" cellspacing="0">'];
5139             for(var i = 0; i < 6; i++){
5140                 buf.push(
5141                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5142                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5143                     i == 0 ?
5144                     '<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>' :
5145                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5146                 );
5147             }
5148             buf.push(
5149                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5150                     this.okText,
5151                     '</button><button type="button" class="x-date-mp-cancel">',
5152                     this.cancelText,
5153                     '</button></td></tr>',
5154                 '</table>'
5155             );
5156             this.monthPicker.update(buf.join(''));
5157             this.monthPicker.on('click', this.onMonthClick, this);
5158             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5159
5160             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5161             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5162
5163             this.mpMonths.each(function(m, a, i){
5164                 i += 1;
5165                 if((i%2) == 0){
5166                     m.dom.xmonth = 5 + Math.round(i * .5);
5167                 }else{
5168                     m.dom.xmonth = Math.round((i-1) * .5);
5169                 }
5170             });
5171         }
5172     },
5173
5174     showMonthPicker : function(){
5175         this.createMonthPicker();
5176         var size = this.el.getSize();
5177         this.monthPicker.setSize(size);
5178         this.monthPicker.child('table').setSize(size);
5179
5180         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5181         this.updateMPMonth(this.mpSelMonth);
5182         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5183         this.updateMPYear(this.mpSelYear);
5184
5185         this.monthPicker.slideIn('t', {duration:.2});
5186     },
5187
5188     updateMPYear : function(y){
5189         this.mpyear = y;
5190         var ys = this.mpYears.elements;
5191         for(var i = 1; i <= 10; i++){
5192             var td = ys[i-1], y2;
5193             if((i%2) == 0){
5194                 y2 = y + Math.round(i * .5);
5195                 td.firstChild.innerHTML = y2;
5196                 td.xyear = y2;
5197             }else{
5198                 y2 = y - (5-Math.round(i * .5));
5199                 td.firstChild.innerHTML = y2;
5200                 td.xyear = y2;
5201             }
5202             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5203         }
5204     },
5205
5206     updateMPMonth : function(sm){
5207         this.mpMonths.each(function(m, a, i){
5208             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5209         });
5210     },
5211
5212     selectMPMonth: function(m){
5213         
5214     },
5215
5216     onMonthClick : function(e, t){
5217         e.stopEvent();
5218         var el = new Roo.Element(t), pn;
5219         if(el.is('button.x-date-mp-cancel')){
5220             this.hideMonthPicker();
5221         }
5222         else if(el.is('button.x-date-mp-ok')){
5223             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5224             this.hideMonthPicker();
5225         }
5226         else if(pn = el.up('td.x-date-mp-month', 2)){
5227             this.mpMonths.removeClass('x-date-mp-sel');
5228             pn.addClass('x-date-mp-sel');
5229             this.mpSelMonth = pn.dom.xmonth;
5230         }
5231         else if(pn = el.up('td.x-date-mp-year', 2)){
5232             this.mpYears.removeClass('x-date-mp-sel');
5233             pn.addClass('x-date-mp-sel');
5234             this.mpSelYear = pn.dom.xyear;
5235         }
5236         else if(el.is('a.x-date-mp-prev')){
5237             this.updateMPYear(this.mpyear-10);
5238         }
5239         else if(el.is('a.x-date-mp-next')){
5240             this.updateMPYear(this.mpyear+10);
5241         }
5242     },
5243
5244     onMonthDblClick : function(e, t){
5245         e.stopEvent();
5246         var el = new Roo.Element(t), pn;
5247         if(pn = el.up('td.x-date-mp-month', 2)){
5248             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5249             this.hideMonthPicker();
5250         }
5251         else if(pn = el.up('td.x-date-mp-year', 2)){
5252             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5253             this.hideMonthPicker();
5254         }
5255     },
5256
5257     hideMonthPicker : function(disableAnim){
5258         if(this.monthPicker){
5259             if(disableAnim === true){
5260                 this.monthPicker.hide();
5261             }else{
5262                 this.monthPicker.slideOut('t', {duration:.2});
5263             }
5264         }
5265     },
5266
5267     // private
5268     showPrevMonth : function(e){
5269         this.update(this.activeDate.add("mo", -1));
5270     },
5271
5272     // private
5273     showNextMonth : function(e){
5274         this.update(this.activeDate.add("mo", 1));
5275     },
5276
5277     // private
5278     showPrevYear : function(){
5279         this.update(this.activeDate.add("y", -1));
5280     },
5281
5282     // private
5283     showNextYear : function(){
5284         this.update(this.activeDate.add("y", 1));
5285     },
5286
5287     // private
5288     handleMouseWheel : function(e){
5289         var delta = e.getWheelDelta();
5290         if(delta > 0){
5291             this.showPrevMonth();
5292             e.stopEvent();
5293         } else if(delta < 0){
5294             this.showNextMonth();
5295             e.stopEvent();
5296         }
5297     },
5298
5299     // private
5300     handleDateClick : function(e, t){
5301         e.stopEvent();
5302         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5303             this.setValue(new Date(t.dateValue));
5304             this.fireEvent("select", this, this.value);
5305         }
5306     },
5307
5308     // private
5309     selectToday : function(){
5310         this.setValue(new Date().clearTime());
5311         this.fireEvent("select", this, this.value);
5312     },
5313
5314     // private
5315     update : function(date)
5316     {
5317         var vd = this.activeDate;
5318         this.activeDate = date;
5319         if(vd && this.el){
5320             var t = date.getTime();
5321             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5322                 this.cells.removeClass("x-date-selected");
5323                 this.cells.each(function(c){
5324                    if(c.dom.firstChild.dateValue == t){
5325                        c.addClass("x-date-selected");
5326                        setTimeout(function(){
5327                             try{c.dom.firstChild.focus();}catch(e){}
5328                        }, 50);
5329                        return false;
5330                    }
5331                 });
5332                 return;
5333             }
5334         }
5335         
5336         var days = date.getDaysInMonth();
5337         var firstOfMonth = date.getFirstDateOfMonth();
5338         var startingPos = firstOfMonth.getDay()-this.startDay;
5339
5340         if(startingPos <= this.startDay){
5341             startingPos += 7;
5342         }
5343
5344         var pm = date.add("mo", -1);
5345         var prevStart = pm.getDaysInMonth()-startingPos;
5346
5347         var cells = this.cells.elements;
5348         var textEls = this.textNodes;
5349         days += startingPos;
5350
5351         // convert everything to numbers so it's fast
5352         var day = 86400000;
5353         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5354         var today = new Date().clearTime().getTime();
5355         var sel = date.clearTime().getTime();
5356         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5357         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5358         var ddMatch = this.disabledDatesRE;
5359         var ddText = this.disabledDatesText;
5360         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5361         var ddaysText = this.disabledDaysText;
5362         var format = this.format;
5363
5364         var setCellClass = function(cal, cell){
5365             cell.title = "";
5366             var t = d.getTime();
5367             cell.firstChild.dateValue = t;
5368             if(t == today){
5369                 cell.className += " x-date-today";
5370                 cell.title = cal.todayText;
5371             }
5372             if(t == sel){
5373                 cell.className += " x-date-selected";
5374                 setTimeout(function(){
5375                     try{cell.firstChild.focus();}catch(e){}
5376                 }, 50);
5377             }
5378             // disabling
5379             if(t < min) {
5380                 cell.className = " x-date-disabled";
5381                 cell.title = cal.minText;
5382                 return;
5383             }
5384             if(t > max) {
5385                 cell.className = " x-date-disabled";
5386                 cell.title = cal.maxText;
5387                 return;
5388             }
5389             if(ddays){
5390                 if(ddays.indexOf(d.getDay()) != -1){
5391                     cell.title = ddaysText;
5392                     cell.className = " x-date-disabled";
5393                 }
5394             }
5395             if(ddMatch && format){
5396                 var fvalue = d.dateFormat(format);
5397                 if(ddMatch.test(fvalue)){
5398                     cell.title = ddText.replace("%0", fvalue);
5399                     cell.className = " x-date-disabled";
5400                 }
5401             }
5402         };
5403
5404         var i = 0;
5405         for(; i < startingPos; i++) {
5406             textEls[i].innerHTML = (++prevStart);
5407             d.setDate(d.getDate()+1);
5408             cells[i].className = "x-date-prevday";
5409             setCellClass(this, cells[i]);
5410         }
5411         for(; i < days; i++){
5412             intDay = i - startingPos + 1;
5413             textEls[i].innerHTML = (intDay);
5414             d.setDate(d.getDate()+1);
5415             cells[i].className = "x-date-active";
5416             setCellClass(this, cells[i]);
5417         }
5418         var extraDays = 0;
5419         for(; i < 42; i++) {
5420              textEls[i].innerHTML = (++extraDays);
5421              d.setDate(d.getDate()+1);
5422              cells[i].className = "x-date-nextday";
5423              setCellClass(this, cells[i]);
5424         }
5425
5426         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5427         this.fireEvent('monthchange', this, date);
5428         
5429         if(!this.internalRender){
5430             var main = this.el.dom.firstChild;
5431             var w = main.offsetWidth;
5432             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5433             Roo.fly(main).setWidth(w);
5434             this.internalRender = true;
5435             // opera does not respect the auto grow header center column
5436             // then, after it gets a width opera refuses to recalculate
5437             // without a second pass
5438             if(Roo.isOpera && !this.secondPass){
5439                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5440                 this.secondPass = true;
5441                 this.update.defer(10, this, [date]);
5442             }
5443         }
5444         
5445         
5446     }
5447 });        /*
5448  * Based on:
5449  * Ext JS Library 1.1.1
5450  * Copyright(c) 2006-2007, Ext JS, LLC.
5451  *
5452  * Originally Released Under LGPL - original licence link has changed is not relivant.
5453  *
5454  * Fork - LGPL
5455  * <script type="text/javascript">
5456  */
5457 /**
5458  * @class Roo.TabPanel
5459  * @extends Roo.util.Observable
5460  * A lightweight tab container.
5461  * <br><br>
5462  * Usage:
5463  * <pre><code>
5464 // basic tabs 1, built from existing content
5465 var tabs = new Roo.TabPanel("tabs1");
5466 tabs.addTab("script", "View Script");
5467 tabs.addTab("markup", "View Markup");
5468 tabs.activate("script");
5469
5470 // more advanced tabs, built from javascript
5471 var jtabs = new Roo.TabPanel("jtabs");
5472 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5473
5474 // set up the UpdateManager
5475 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5476 var updater = tab2.getUpdateManager();
5477 updater.setDefaultUrl("ajax1.htm");
5478 tab2.on('activate', updater.refresh, updater, true);
5479
5480 // Use setUrl for Ajax loading
5481 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5482 tab3.setUrl("ajax2.htm", null, true);
5483
5484 // Disabled tab
5485 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5486 tab4.disable();
5487
5488 jtabs.activate("jtabs-1");
5489  * </code></pre>
5490  * @constructor
5491  * Create a new TabPanel.
5492  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5493  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5494  */
5495 Roo.TabPanel = function(container, config){
5496     /**
5497     * The container element for this TabPanel.
5498     * @type Roo.Element
5499     */
5500     this.el = Roo.get(container, true);
5501     if(config){
5502         if(typeof config == "boolean"){
5503             this.tabPosition = config ? "bottom" : "top";
5504         }else{
5505             Roo.apply(this, config);
5506         }
5507     }
5508     if(this.tabPosition == "bottom"){
5509         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5510         this.el.addClass("x-tabs-bottom");
5511     }
5512     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5513     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5514     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5515     if(Roo.isIE){
5516         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5517     }
5518     if(this.tabPosition != "bottom"){
5519         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5520          * @type Roo.Element
5521          */
5522         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5523         this.el.addClass("x-tabs-top");
5524     }
5525     this.items = [];
5526
5527     this.bodyEl.setStyle("position", "relative");
5528
5529     this.active = null;
5530     this.activateDelegate = this.activate.createDelegate(this);
5531
5532     this.addEvents({
5533         /**
5534          * @event tabchange
5535          * Fires when the active tab changes
5536          * @param {Roo.TabPanel} this
5537          * @param {Roo.TabPanelItem} activePanel The new active tab
5538          */
5539         "tabchange": true,
5540         /**
5541          * @event beforetabchange
5542          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5543          * @param {Roo.TabPanel} this
5544          * @param {Object} e Set cancel to true on this object to cancel the tab change
5545          * @param {Roo.TabPanelItem} tab The tab being changed to
5546          */
5547         "beforetabchange" : true
5548     });
5549
5550     Roo.EventManager.onWindowResize(this.onResize, this);
5551     this.cpad = this.el.getPadding("lr");
5552     this.hiddenCount = 0;
5553
5554
5555     // toolbar on the tabbar support...
5556     if (this.toolbar) {
5557         var tcfg = this.toolbar;
5558         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
5559         this.toolbar = new Roo.Toolbar(tcfg);
5560         if (Roo.isSafari) {
5561             var tbl = tcfg.container.child('table', true);
5562             tbl.setAttribute('width', '100%');
5563         }
5564         
5565     }
5566    
5567
5568
5569     Roo.TabPanel.superclass.constructor.call(this);
5570 };
5571
5572 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5573     /*
5574      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5575      */
5576     tabPosition : "top",
5577     /*
5578      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5579      */
5580     currentTabWidth : 0,
5581     /*
5582      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5583      */
5584     minTabWidth : 40,
5585     /*
5586      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5587      */
5588     maxTabWidth : 250,
5589     /*
5590      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5591      */
5592     preferredTabWidth : 175,
5593     /*
5594      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
5595      */
5596     resizeTabs : false,
5597     /*
5598      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
5599      */
5600     monitorResize : true,
5601     /*
5602      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
5603      */
5604     toolbar : false,
5605
5606     /**
5607      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
5608      * @param {String} id The id of the div to use <b>or create</b>
5609      * @param {String} text The text for the tab
5610      * @param {String} content (optional) Content to put in the TabPanelItem body
5611      * @param {Boolean} closable (optional) True to create a close icon on the tab
5612      * @return {Roo.TabPanelItem} The created TabPanelItem
5613      */
5614     addTab : function(id, text, content, closable){
5615         var item = new Roo.TabPanelItem(this, id, text, closable);
5616         this.addTabItem(item);
5617         if(content){
5618             item.setContent(content);
5619         }
5620         return item;
5621     },
5622
5623     /**
5624      * Returns the {@link Roo.TabPanelItem} with the specified id/index
5625      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
5626      * @return {Roo.TabPanelItem}
5627      */
5628     getTab : function(id){
5629         return this.items[id];
5630     },
5631
5632     /**
5633      * Hides the {@link Roo.TabPanelItem} with the specified id/index
5634      * @param {String/Number} id The id or index of the TabPanelItem to hide.
5635      */
5636     hideTab : function(id){
5637         var t = this.items[id];
5638         if(!t.isHidden()){
5639            t.setHidden(true);
5640            this.hiddenCount++;
5641            this.autoSizeTabs();
5642         }
5643     },
5644
5645     /**
5646      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
5647      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
5648      */
5649     unhideTab : function(id){
5650         var t = this.items[id];
5651         if(t.isHidden()){
5652            t.setHidden(false);
5653            this.hiddenCount--;
5654            this.autoSizeTabs();
5655         }
5656     },
5657
5658     /**
5659      * Adds an existing {@link Roo.TabPanelItem}.
5660      * @param {Roo.TabPanelItem} item The TabPanelItem to add
5661      */
5662     addTabItem : function(item){
5663         this.items[item.id] = item;
5664         this.items.push(item);
5665         if(this.resizeTabs){
5666            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
5667            this.autoSizeTabs();
5668         }else{
5669             item.autoSize();
5670         }
5671     },
5672
5673     /**
5674      * Removes a {@link Roo.TabPanelItem}.
5675      * @param {String/Number} id The id or index of the TabPanelItem to remove.
5676      */
5677     removeTab : function(id){
5678         var items = this.items;
5679         var tab = items[id];
5680         if(!tab) { return; }
5681         var index = items.indexOf(tab);
5682         if(this.active == tab && items.length > 1){
5683             var newTab = this.getNextAvailable(index);
5684             if(newTab) {
5685                 newTab.activate();
5686             }
5687         }
5688         this.stripEl.dom.removeChild(tab.pnode.dom);
5689         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
5690             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
5691         }
5692         items.splice(index, 1);
5693         delete this.items[tab.id];
5694         tab.fireEvent("close", tab);
5695         tab.purgeListeners();
5696         this.autoSizeTabs();
5697     },
5698
5699     getNextAvailable : function(start){
5700         var items = this.items;
5701         var index = start;
5702         // look for a next tab that will slide over to
5703         // replace the one being removed
5704         while(index < items.length){
5705             var item = items[++index];
5706             if(item && !item.isHidden()){
5707                 return item;
5708             }
5709         }
5710         // if one isn't found select the previous tab (on the left)
5711         index = start;
5712         while(index >= 0){
5713             var item = items[--index];
5714             if(item && !item.isHidden()){
5715                 return item;
5716             }
5717         }
5718         return null;
5719     },
5720
5721     /**
5722      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
5723      * @param {String/Number} id The id or index of the TabPanelItem to disable.
5724      */
5725     disableTab : function(id){
5726         var tab = this.items[id];
5727         if(tab && this.active != tab){
5728             tab.disable();
5729         }
5730     },
5731
5732     /**
5733      * Enables a {@link Roo.TabPanelItem} that is disabled.
5734      * @param {String/Number} id The id or index of the TabPanelItem to enable.
5735      */
5736     enableTab : function(id){
5737         var tab = this.items[id];
5738         tab.enable();
5739     },
5740
5741     /**
5742      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
5743      * @param {String/Number} id The id or index of the TabPanelItem to activate.
5744      * @return {Roo.TabPanelItem} The TabPanelItem.
5745      */
5746     activate : function(id){
5747         var tab = this.items[id];
5748         if(!tab){
5749             return null;
5750         }
5751         if(tab == this.active || tab.disabled){
5752             return tab;
5753         }
5754         var e = {};
5755         this.fireEvent("beforetabchange", this, e, tab);
5756         if(e.cancel !== true && !tab.disabled){
5757             if(this.active){
5758                 this.active.hide();
5759             }
5760             this.active = this.items[id];
5761             this.active.show();
5762             this.fireEvent("tabchange", this, this.active);
5763         }
5764         return tab;
5765     },
5766
5767     /**
5768      * Gets the active {@link Roo.TabPanelItem}.
5769      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
5770      */
5771     getActiveTab : function(){
5772         return this.active;
5773     },
5774
5775     /**
5776      * Updates the tab body element to fit the height of the container element
5777      * for overflow scrolling
5778      * @param {Number} targetHeight (optional) Override the starting height from the elements height
5779      */
5780     syncHeight : function(targetHeight){
5781         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
5782         var bm = this.bodyEl.getMargins();
5783         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
5784         this.bodyEl.setHeight(newHeight);
5785         return newHeight;
5786     },
5787
5788     onResize : function(){
5789         if(this.monitorResize){
5790             this.autoSizeTabs();
5791         }
5792     },
5793
5794     /**
5795      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
5796      */
5797     beginUpdate : function(){
5798         this.updating = true;
5799     },
5800
5801     /**
5802      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
5803      */
5804     endUpdate : function(){
5805         this.updating = false;
5806         this.autoSizeTabs();
5807     },
5808
5809     /**
5810      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
5811      */
5812     autoSizeTabs : function(){
5813         var count = this.items.length;
5814         var vcount = count - this.hiddenCount;
5815         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
5816             return;
5817         }
5818         var w = Math.max(this.el.getWidth() - this.cpad, 10);
5819         var availWidth = Math.floor(w / vcount);
5820         var b = this.stripBody;
5821         if(b.getWidth() > w){
5822             var tabs = this.items;
5823             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
5824             if(availWidth < this.minTabWidth){
5825                 /*if(!this.sleft){    // incomplete scrolling code
5826                     this.createScrollButtons();
5827                 }
5828                 this.showScroll();
5829                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
5830             }
5831         }else{
5832             if(this.currentTabWidth < this.preferredTabWidth){
5833                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
5834             }
5835         }
5836     },
5837
5838     /**
5839      * Returns the number of tabs in this TabPanel.
5840      * @return {Number}
5841      */
5842      getCount : function(){
5843          return this.items.length;
5844      },
5845
5846     /**
5847      * Resizes all the tabs to the passed width
5848      * @param {Number} The new width
5849      */
5850     setTabWidth : function(width){
5851         this.currentTabWidth = width;
5852         for(var i = 0, len = this.items.length; i < len; i++) {
5853                 if(!this.items[i].isHidden()) {
5854                 this.items[i].setWidth(width);
5855             }
5856         }
5857     },
5858
5859     /**
5860      * Destroys this TabPanel
5861      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
5862      */
5863     destroy : function(removeEl){
5864         Roo.EventManager.removeResizeListener(this.onResize, this);
5865         for(var i = 0, len = this.items.length; i < len; i++){
5866             this.items[i].purgeListeners();
5867         }
5868         if(removeEl === true){
5869             this.el.update("");
5870             this.el.remove();
5871         }
5872     }
5873 });
5874
5875 /**
5876  * @class Roo.TabPanelItem
5877  * @extends Roo.util.Observable
5878  * Represents an individual item (tab plus body) in a TabPanel.
5879  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
5880  * @param {String} id The id of this TabPanelItem
5881  * @param {String} text The text for the tab of this TabPanelItem
5882  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
5883  */
5884 Roo.TabPanelItem = function(tabPanel, id, text, closable){
5885     /**
5886      * The {@link Roo.TabPanel} this TabPanelItem belongs to
5887      * @type Roo.TabPanel
5888      */
5889     this.tabPanel = tabPanel;
5890     /**
5891      * The id for this TabPanelItem
5892      * @type String
5893      */
5894     this.id = id;
5895     /** @private */
5896     this.disabled = false;
5897     /** @private */
5898     this.text = text;
5899     /** @private */
5900     this.loaded = false;
5901     this.closable = closable;
5902
5903     /**
5904      * The body element for this TabPanelItem.
5905      * @type Roo.Element
5906      */
5907     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
5908     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
5909     this.bodyEl.setStyle("display", "block");
5910     this.bodyEl.setStyle("zoom", "1");
5911     this.hideAction();
5912
5913     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
5914     /** @private */
5915     this.el = Roo.get(els.el, true);
5916     this.inner = Roo.get(els.inner, true);
5917     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
5918     this.pnode = Roo.get(els.el.parentNode, true);
5919     this.el.on("mousedown", this.onTabMouseDown, this);
5920     this.el.on("click", this.onTabClick, this);
5921     /** @private */
5922     if(closable){
5923         var c = Roo.get(els.close, true);
5924         c.dom.title = this.closeText;
5925         c.addClassOnOver("close-over");
5926         c.on("click", this.closeClick, this);
5927      }
5928
5929     this.addEvents({
5930          /**
5931          * @event activate
5932          * Fires when this tab becomes the active tab.
5933          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5934          * @param {Roo.TabPanelItem} this
5935          */
5936         "activate": true,
5937         /**
5938          * @event beforeclose
5939          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
5940          * @param {Roo.TabPanelItem} this
5941          * @param {Object} e Set cancel to true on this object to cancel the close.
5942          */
5943         "beforeclose": true,
5944         /**
5945          * @event close
5946          * Fires when this tab is closed.
5947          * @param {Roo.TabPanelItem} this
5948          */
5949          "close": true,
5950         /**
5951          * @event deactivate
5952          * Fires when this tab is no longer the active tab.
5953          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5954          * @param {Roo.TabPanelItem} this
5955          */
5956          "deactivate" : true
5957     });
5958     this.hidden = false;
5959
5960     Roo.TabPanelItem.superclass.constructor.call(this);
5961 };
5962
5963 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
5964     purgeListeners : function(){
5965        Roo.util.Observable.prototype.purgeListeners.call(this);
5966        this.el.removeAllListeners();
5967     },
5968     /**
5969      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
5970      */
5971     show : function(){
5972         this.pnode.addClass("on");
5973         this.showAction();
5974         if(Roo.isOpera){
5975             this.tabPanel.stripWrap.repaint();
5976         }
5977         this.fireEvent("activate", this.tabPanel, this);
5978     },
5979
5980     /**
5981      * Returns true if this tab is the active tab.
5982      * @return {Boolean}
5983      */
5984     isActive : function(){
5985         return this.tabPanel.getActiveTab() == this;
5986     },
5987
5988     /**
5989      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
5990      */
5991     hide : function(){
5992         this.pnode.removeClass("on");
5993         this.hideAction();
5994         this.fireEvent("deactivate", this.tabPanel, this);
5995     },
5996
5997     hideAction : function(){
5998         this.bodyEl.hide();
5999         this.bodyEl.setStyle("position", "absolute");
6000         this.bodyEl.setLeft("-20000px");
6001         this.bodyEl.setTop("-20000px");
6002     },
6003
6004     showAction : function(){
6005         this.bodyEl.setStyle("position", "relative");
6006         this.bodyEl.setTop("");
6007         this.bodyEl.setLeft("");
6008         this.bodyEl.show();
6009     },
6010
6011     /**
6012      * Set the tooltip for the tab.
6013      * @param {String} tooltip The tab's tooltip
6014      */
6015     setTooltip : function(text){
6016         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6017             this.textEl.dom.qtip = text;
6018             this.textEl.dom.removeAttribute('title');
6019         }else{
6020             this.textEl.dom.title = text;
6021         }
6022     },
6023
6024     onTabClick : function(e){
6025         e.preventDefault();
6026         this.tabPanel.activate(this.id);
6027     },
6028
6029     onTabMouseDown : function(e){
6030         e.preventDefault();
6031         this.tabPanel.activate(this.id);
6032     },
6033
6034     getWidth : function(){
6035         return this.inner.getWidth();
6036     },
6037
6038     setWidth : function(width){
6039         var iwidth = width - this.pnode.getPadding("lr");
6040         this.inner.setWidth(iwidth);
6041         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6042         this.pnode.setWidth(width);
6043     },
6044
6045     /**
6046      * Show or hide the tab
6047      * @param {Boolean} hidden True to hide or false to show.
6048      */
6049     setHidden : function(hidden){
6050         this.hidden = hidden;
6051         this.pnode.setStyle("display", hidden ? "none" : "");
6052     },
6053
6054     /**
6055      * Returns true if this tab is "hidden"
6056      * @return {Boolean}
6057      */
6058     isHidden : function(){
6059         return this.hidden;
6060     },
6061
6062     /**
6063      * Returns the text for this tab
6064      * @return {String}
6065      */
6066     getText : function(){
6067         return this.text;
6068     },
6069
6070     autoSize : function(){
6071         //this.el.beginMeasure();
6072         this.textEl.setWidth(1);
6073         /*
6074          *  #2804 [new] Tabs in Roojs
6075          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6076          */
6077         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6078         //this.el.endMeasure();
6079     },
6080
6081     /**
6082      * Sets the text for the tab (Note: this also sets the tooltip text)
6083      * @param {String} text The tab's text and tooltip
6084      */
6085     setText : function(text){
6086         this.text = text;
6087         this.textEl.update(text);
6088         this.setTooltip(text);
6089         if(!this.tabPanel.resizeTabs){
6090             this.autoSize();
6091         }
6092     },
6093     /**
6094      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6095      */
6096     activate : function(){
6097         this.tabPanel.activate(this.id);
6098     },
6099
6100     /**
6101      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6102      */
6103     disable : function(){
6104         if(this.tabPanel.active != this){
6105             this.disabled = true;
6106             this.pnode.addClass("disabled");
6107         }
6108     },
6109
6110     /**
6111      * Enables this TabPanelItem if it was previously disabled.
6112      */
6113     enable : function(){
6114         this.disabled = false;
6115         this.pnode.removeClass("disabled");
6116     },
6117
6118     /**
6119      * Sets the content for this TabPanelItem.
6120      * @param {String} content The content
6121      * @param {Boolean} loadScripts true to look for and load scripts
6122      */
6123     setContent : function(content, loadScripts){
6124         this.bodyEl.update(content, loadScripts);
6125     },
6126
6127     /**
6128      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6129      * @return {Roo.UpdateManager} The UpdateManager
6130      */
6131     getUpdateManager : function(){
6132         return this.bodyEl.getUpdateManager();
6133     },
6134
6135     /**
6136      * Set a URL to be used to load the content for this TabPanelItem.
6137      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6138      * @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)
6139      * @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)
6140      * @return {Roo.UpdateManager} The UpdateManager
6141      */
6142     setUrl : function(url, params, loadOnce){
6143         if(this.refreshDelegate){
6144             this.un('activate', this.refreshDelegate);
6145         }
6146         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6147         this.on("activate", this.refreshDelegate);
6148         return this.bodyEl.getUpdateManager();
6149     },
6150
6151     /** @private */
6152     _handleRefresh : function(url, params, loadOnce){
6153         if(!loadOnce || !this.loaded){
6154             var updater = this.bodyEl.getUpdateManager();
6155             updater.update(url, params, this._setLoaded.createDelegate(this));
6156         }
6157     },
6158
6159     /**
6160      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6161      *   Will fail silently if the setUrl method has not been called.
6162      *   This does not activate the panel, just updates its content.
6163      */
6164     refresh : function(){
6165         if(this.refreshDelegate){
6166            this.loaded = false;
6167            this.refreshDelegate();
6168         }
6169     },
6170
6171     /** @private */
6172     _setLoaded : function(){
6173         this.loaded = true;
6174     },
6175
6176     /** @private */
6177     closeClick : function(e){
6178         var o = {};
6179         e.stopEvent();
6180         this.fireEvent("beforeclose", this, o);
6181         if(o.cancel !== true){
6182             this.tabPanel.removeTab(this.id);
6183         }
6184     },
6185     /**
6186      * The text displayed in the tooltip for the close icon.
6187      * @type String
6188      */
6189     closeText : "Close this tab"
6190 });
6191
6192 /** @private */
6193 Roo.TabPanel.prototype.createStrip = function(container){
6194     var strip = document.createElement("div");
6195     strip.className = "x-tabs-wrap";
6196     container.appendChild(strip);
6197     return strip;
6198 };
6199 /** @private */
6200 Roo.TabPanel.prototype.createStripList = function(strip){
6201     // div wrapper for retard IE
6202     // returns the "tr" element.
6203     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6204         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6205         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6206     return strip.firstChild.firstChild.firstChild.firstChild;
6207 };
6208 /** @private */
6209 Roo.TabPanel.prototype.createBody = function(container){
6210     var body = document.createElement("div");
6211     Roo.id(body, "tab-body");
6212     Roo.fly(body).addClass("x-tabs-body");
6213     container.appendChild(body);
6214     return body;
6215 };
6216 /** @private */
6217 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6218     var body = Roo.getDom(id);
6219     if(!body){
6220         body = document.createElement("div");
6221         body.id = id;
6222     }
6223     Roo.fly(body).addClass("x-tabs-item-body");
6224     bodyEl.insertBefore(body, bodyEl.firstChild);
6225     return body;
6226 };
6227 /** @private */
6228 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6229     var td = document.createElement("td");
6230     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6231     //stripEl.appendChild(td);
6232     if(closable){
6233         td.className = "x-tabs-closable";
6234         if(!this.closeTpl){
6235             this.closeTpl = new Roo.Template(
6236                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6237                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6238                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6239             );
6240         }
6241         var el = this.closeTpl.overwrite(td, {"text": text});
6242         var close = el.getElementsByTagName("div")[0];
6243         var inner = el.getElementsByTagName("em")[0];
6244         return {"el": el, "close": close, "inner": inner};
6245     } else {
6246         if(!this.tabTpl){
6247             this.tabTpl = new Roo.Template(
6248                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6249                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6250             );
6251         }
6252         var el = this.tabTpl.overwrite(td, {"text": text});
6253         var inner = el.getElementsByTagName("em")[0];
6254         return {"el": el, "inner": inner};
6255     }
6256 };/*
6257  * Based on:
6258  * Ext JS Library 1.1.1
6259  * Copyright(c) 2006-2007, Ext JS, LLC.
6260  *
6261  * Originally Released Under LGPL - original licence link has changed is not relivant.
6262  *
6263  * Fork - LGPL
6264  * <script type="text/javascript">
6265  */
6266
6267 /**
6268  * @class Roo.Button
6269  * @extends Roo.util.Observable
6270  * Simple Button class
6271  * @cfg {String} text The button text
6272  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6273  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6274  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6275  * @cfg {Object} scope The scope of the handler
6276  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6277  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6278  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6279  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6280  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6281  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6282    applies if enableToggle = true)
6283  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6284  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6285   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6286  * @constructor
6287  * Create a new button
6288  * @param {Object} config The config object
6289  */
6290 Roo.Button = function(renderTo, config)
6291 {
6292     if (!config) {
6293         config = renderTo;
6294         renderTo = config.renderTo || false;
6295     }
6296     
6297     Roo.apply(this, config);
6298     this.addEvents({
6299         /**
6300              * @event click
6301              * Fires when this button is clicked
6302              * @param {Button} this
6303              * @param {EventObject} e The click event
6304              */
6305             "click" : true,
6306         /**
6307              * @event toggle
6308              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6309              * @param {Button} this
6310              * @param {Boolean} pressed
6311              */
6312             "toggle" : true,
6313         /**
6314              * @event mouseover
6315              * Fires when the mouse hovers over the button
6316              * @param {Button} this
6317              * @param {Event} e The event object
6318              */
6319         'mouseover' : true,
6320         /**
6321              * @event mouseout
6322              * Fires when the mouse exits the button
6323              * @param {Button} this
6324              * @param {Event} e The event object
6325              */
6326         'mouseout': true,
6327          /**
6328              * @event render
6329              * Fires when the button is rendered
6330              * @param {Button} this
6331              */
6332         'render': true
6333     });
6334     if(this.menu){
6335         this.menu = Roo.menu.MenuMgr.get(this.menu);
6336     }
6337     // register listeners first!!  - so render can be captured..
6338     Roo.util.Observable.call(this);
6339     if(renderTo){
6340         this.render(renderTo);
6341     }
6342     
6343   
6344 };
6345
6346 Roo.extend(Roo.Button, Roo.util.Observable, {
6347     /**
6348      * 
6349      */
6350     
6351     /**
6352      * Read-only. True if this button is hidden
6353      * @type Boolean
6354      */
6355     hidden : false,
6356     /**
6357      * Read-only. True if this button is disabled
6358      * @type Boolean
6359      */
6360     disabled : false,
6361     /**
6362      * Read-only. True if this button is pressed (only if enableToggle = true)
6363      * @type Boolean
6364      */
6365     pressed : false,
6366
6367     /**
6368      * @cfg {Number} tabIndex 
6369      * The DOM tabIndex for this button (defaults to undefined)
6370      */
6371     tabIndex : undefined,
6372
6373     /**
6374      * @cfg {Boolean} enableToggle
6375      * True to enable pressed/not pressed toggling (defaults to false)
6376      */
6377     enableToggle: false,
6378     /**
6379      * @cfg {Roo.menu.Menu} menu
6380      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6381      */
6382     menu : undefined,
6383     /**
6384      * @cfg {String} menuAlign
6385      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6386      */
6387     menuAlign : "tl-bl?",
6388
6389     /**
6390      * @cfg {String} iconCls
6391      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6392      */
6393     iconCls : undefined,
6394     /**
6395      * @cfg {String} type
6396      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6397      */
6398     type : 'button',
6399
6400     // private
6401     menuClassTarget: 'tr',
6402
6403     /**
6404      * @cfg {String} clickEvent
6405      * The type of event to map to the button's event handler (defaults to 'click')
6406      */
6407     clickEvent : 'click',
6408
6409     /**
6410      * @cfg {Boolean} handleMouseEvents
6411      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6412      */
6413     handleMouseEvents : true,
6414
6415     /**
6416      * @cfg {String} tooltipType
6417      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6418      */
6419     tooltipType : 'qtip',
6420
6421     /**
6422      * @cfg {String} cls
6423      * A CSS class to apply to the button's main element.
6424      */
6425     
6426     /**
6427      * @cfg {Roo.Template} template (Optional)
6428      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6429      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6430      * require code modifications if required elements (e.g. a button) aren't present.
6431      */
6432
6433     // private
6434     render : function(renderTo){
6435         var btn;
6436         if(this.hideParent){
6437             this.parentEl = Roo.get(renderTo);
6438         }
6439         if(!this.dhconfig){
6440             if(!this.template){
6441                 if(!Roo.Button.buttonTemplate){
6442                     // hideous table template
6443                     Roo.Button.buttonTemplate = new Roo.Template(
6444                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6445                         '<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>',
6446                         "</tr></tbody></table>");
6447                 }
6448                 this.template = Roo.Button.buttonTemplate;
6449             }
6450             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6451             var btnEl = btn.child("button:first");
6452             btnEl.on('focus', this.onFocus, this);
6453             btnEl.on('blur', this.onBlur, this);
6454             if(this.cls){
6455                 btn.addClass(this.cls);
6456             }
6457             if(this.icon){
6458                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6459             }
6460             if(this.iconCls){
6461                 btnEl.addClass(this.iconCls);
6462                 if(!this.cls){
6463                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6464                 }
6465             }
6466             if(this.tabIndex !== undefined){
6467                 btnEl.dom.tabIndex = this.tabIndex;
6468             }
6469             if(this.tooltip){
6470                 if(typeof this.tooltip == 'object'){
6471                     Roo.QuickTips.tips(Roo.apply({
6472                           target: btnEl.id
6473                     }, this.tooltip));
6474                 } else {
6475                     btnEl.dom[this.tooltipType] = this.tooltip;
6476                 }
6477             }
6478         }else{
6479             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6480         }
6481         this.el = btn;
6482         if(this.id){
6483             this.el.dom.id = this.el.id = this.id;
6484         }
6485         if(this.menu){
6486             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6487             this.menu.on("show", this.onMenuShow, this);
6488             this.menu.on("hide", this.onMenuHide, this);
6489         }
6490         btn.addClass("x-btn");
6491         if(Roo.isIE && !Roo.isIE7){
6492             this.autoWidth.defer(1, this);
6493         }else{
6494             this.autoWidth();
6495         }
6496         if(this.handleMouseEvents){
6497             btn.on("mouseover", this.onMouseOver, this);
6498             btn.on("mouseout", this.onMouseOut, this);
6499             btn.on("mousedown", this.onMouseDown, this);
6500         }
6501         btn.on(this.clickEvent, this.onClick, this);
6502         //btn.on("mouseup", this.onMouseUp, this);
6503         if(this.hidden){
6504             this.hide();
6505         }
6506         if(this.disabled){
6507             this.disable();
6508         }
6509         Roo.ButtonToggleMgr.register(this);
6510         if(this.pressed){
6511             this.el.addClass("x-btn-pressed");
6512         }
6513         if(this.repeat){
6514             var repeater = new Roo.util.ClickRepeater(btn,
6515                 typeof this.repeat == "object" ? this.repeat : {}
6516             );
6517             repeater.on("click", this.onClick,  this);
6518         }
6519         
6520         this.fireEvent('render', this);
6521         
6522     },
6523     /**
6524      * Returns the button's underlying element
6525      * @return {Roo.Element} The element
6526      */
6527     getEl : function(){
6528         return this.el;  
6529     },
6530     
6531     /**
6532      * Destroys this Button and removes any listeners.
6533      */
6534     destroy : function(){
6535         Roo.ButtonToggleMgr.unregister(this);
6536         this.el.removeAllListeners();
6537         this.purgeListeners();
6538         this.el.remove();
6539     },
6540
6541     // private
6542     autoWidth : function(){
6543         if(this.el){
6544             this.el.setWidth("auto");
6545             if(Roo.isIE7 && Roo.isStrict){
6546                 var ib = this.el.child('button');
6547                 if(ib && ib.getWidth() > 20){
6548                     ib.clip();
6549                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6550                 }
6551             }
6552             if(this.minWidth){
6553                 if(this.hidden){
6554                     this.el.beginMeasure();
6555                 }
6556                 if(this.el.getWidth() < this.minWidth){
6557                     this.el.setWidth(this.minWidth);
6558                 }
6559                 if(this.hidden){
6560                     this.el.endMeasure();
6561                 }
6562             }
6563         }
6564     },
6565
6566     /**
6567      * Assigns this button's click handler
6568      * @param {Function} handler The function to call when the button is clicked
6569      * @param {Object} scope (optional) Scope for the function passed in
6570      */
6571     setHandler : function(handler, scope){
6572         this.handler = handler;
6573         this.scope = scope;  
6574     },
6575     
6576     /**
6577      * Sets this button's text
6578      * @param {String} text The button text
6579      */
6580     setText : function(text){
6581         this.text = text;
6582         if(this.el){
6583             this.el.child("td.x-btn-center button.x-btn-text").update(text);
6584         }
6585         this.autoWidth();
6586     },
6587     
6588     /**
6589      * Gets the text for this button
6590      * @return {String} The button text
6591      */
6592     getText : function(){
6593         return this.text;  
6594     },
6595     
6596     /**
6597      * Show this button
6598      */
6599     show: function(){
6600         this.hidden = false;
6601         if(this.el){
6602             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
6603         }
6604     },
6605     
6606     /**
6607      * Hide this button
6608      */
6609     hide: function(){
6610         this.hidden = true;
6611         if(this.el){
6612             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
6613         }
6614     },
6615     
6616     /**
6617      * Convenience function for boolean show/hide
6618      * @param {Boolean} visible True to show, false to hide
6619      */
6620     setVisible: function(visible){
6621         if(visible) {
6622             this.show();
6623         }else{
6624             this.hide();
6625         }
6626     },
6627     
6628     /**
6629      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
6630      * @param {Boolean} state (optional) Force a particular state
6631      */
6632     toggle : function(state){
6633         state = state === undefined ? !this.pressed : state;
6634         if(state != this.pressed){
6635             if(state){
6636                 this.el.addClass("x-btn-pressed");
6637                 this.pressed = true;
6638                 this.fireEvent("toggle", this, true);
6639             }else{
6640                 this.el.removeClass("x-btn-pressed");
6641                 this.pressed = false;
6642                 this.fireEvent("toggle", this, false);
6643             }
6644             if(this.toggleHandler){
6645                 this.toggleHandler.call(this.scope || this, this, state);
6646             }
6647         }
6648     },
6649     
6650     /**
6651      * Focus the button
6652      */
6653     focus : function(){
6654         this.el.child('button:first').focus();
6655     },
6656     
6657     /**
6658      * Disable this button
6659      */
6660     disable : function(){
6661         if(this.el){
6662             this.el.addClass("x-btn-disabled");
6663         }
6664         this.disabled = true;
6665     },
6666     
6667     /**
6668      * Enable this button
6669      */
6670     enable : function(){
6671         if(this.el){
6672             this.el.removeClass("x-btn-disabled");
6673         }
6674         this.disabled = false;
6675     },
6676
6677     /**
6678      * Convenience function for boolean enable/disable
6679      * @param {Boolean} enabled True to enable, false to disable
6680      */
6681     setDisabled : function(v){
6682         this[v !== true ? "enable" : "disable"]();
6683     },
6684
6685     // private
6686     onClick : function(e)
6687     {
6688         if(e){
6689             e.preventDefault();
6690         }
6691         if(e.button != 0){
6692             return;
6693         }
6694         if(!this.disabled){
6695             if(this.enableToggle){
6696                 this.toggle();
6697             }
6698             if(this.menu && !this.menu.isVisible()){
6699                 this.menu.show(this.el, this.menuAlign);
6700             }
6701             this.fireEvent("click", this, e);
6702             if(this.handler){
6703                 this.el.removeClass("x-btn-over");
6704                 this.handler.call(this.scope || this, this, e);
6705             }
6706         }
6707     },
6708     // private
6709     onMouseOver : function(e){
6710         if(!this.disabled){
6711             this.el.addClass("x-btn-over");
6712             this.fireEvent('mouseover', this, e);
6713         }
6714     },
6715     // private
6716     onMouseOut : function(e){
6717         if(!e.within(this.el,  true)){
6718             this.el.removeClass("x-btn-over");
6719             this.fireEvent('mouseout', this, e);
6720         }
6721     },
6722     // private
6723     onFocus : function(e){
6724         if(!this.disabled){
6725             this.el.addClass("x-btn-focus");
6726         }
6727     },
6728     // private
6729     onBlur : function(e){
6730         this.el.removeClass("x-btn-focus");
6731     },
6732     // private
6733     onMouseDown : function(e){
6734         if(!this.disabled && e.button == 0){
6735             this.el.addClass("x-btn-click");
6736             Roo.get(document).on('mouseup', this.onMouseUp, this);
6737         }
6738     },
6739     // private
6740     onMouseUp : function(e){
6741         if(e.button == 0){
6742             this.el.removeClass("x-btn-click");
6743             Roo.get(document).un('mouseup', this.onMouseUp, this);
6744         }
6745     },
6746     // private
6747     onMenuShow : function(e){
6748         this.el.addClass("x-btn-menu-active");
6749     },
6750     // private
6751     onMenuHide : function(e){
6752         this.el.removeClass("x-btn-menu-active");
6753     }   
6754 });
6755
6756 // Private utility class used by Button
6757 Roo.ButtonToggleMgr = function(){
6758    var groups = {};
6759    
6760    function toggleGroup(btn, state){
6761        if(state){
6762            var g = groups[btn.toggleGroup];
6763            for(var i = 0, l = g.length; i < l; i++){
6764                if(g[i] != btn){
6765                    g[i].toggle(false);
6766                }
6767            }
6768        }
6769    }
6770    
6771    return {
6772        register : function(btn){
6773            if(!btn.toggleGroup){
6774                return;
6775            }
6776            var g = groups[btn.toggleGroup];
6777            if(!g){
6778                g = groups[btn.toggleGroup] = [];
6779            }
6780            g.push(btn);
6781            btn.on("toggle", toggleGroup);
6782        },
6783        
6784        unregister : function(btn){
6785            if(!btn.toggleGroup){
6786                return;
6787            }
6788            var g = groups[btn.toggleGroup];
6789            if(g){
6790                g.remove(btn);
6791                btn.un("toggle", toggleGroup);
6792            }
6793        }
6794    };
6795 }();/*
6796  * Based on:
6797  * Ext JS Library 1.1.1
6798  * Copyright(c) 2006-2007, Ext JS, LLC.
6799  *
6800  * Originally Released Under LGPL - original licence link has changed is not relivant.
6801  *
6802  * Fork - LGPL
6803  * <script type="text/javascript">
6804  */
6805  
6806 /**
6807  * @class Roo.SplitButton
6808  * @extends Roo.Button
6809  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
6810  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
6811  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
6812  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
6813  * @cfg {String} arrowTooltip The title attribute of the arrow
6814  * @constructor
6815  * Create a new menu button
6816  * @param {String/HTMLElement/Element} renderTo The element to append the button to
6817  * @param {Object} config The config object
6818  */
6819 Roo.SplitButton = function(renderTo, config){
6820     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
6821     /**
6822      * @event arrowclick
6823      * Fires when this button's arrow is clicked
6824      * @param {SplitButton} this
6825      * @param {EventObject} e The click event
6826      */
6827     this.addEvents({"arrowclick":true});
6828 };
6829
6830 Roo.extend(Roo.SplitButton, Roo.Button, {
6831     render : function(renderTo){
6832         // this is one sweet looking template!
6833         var tpl = new Roo.Template(
6834             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
6835             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
6836             '<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>',
6837             "</tbody></table></td><td>",
6838             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
6839             '<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>',
6840             "</tbody></table></td></tr></table>"
6841         );
6842         var btn = tpl.append(renderTo, [this.text, this.type], true);
6843         var btnEl = btn.child("button");
6844         if(this.cls){
6845             btn.addClass(this.cls);
6846         }
6847         if(this.icon){
6848             btnEl.setStyle('background-image', 'url(' +this.icon +')');
6849         }
6850         if(this.iconCls){
6851             btnEl.addClass(this.iconCls);
6852             if(!this.cls){
6853                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6854             }
6855         }
6856         this.el = btn;
6857         if(this.handleMouseEvents){
6858             btn.on("mouseover", this.onMouseOver, this);
6859             btn.on("mouseout", this.onMouseOut, this);
6860             btn.on("mousedown", this.onMouseDown, this);
6861             btn.on("mouseup", this.onMouseUp, this);
6862         }
6863         btn.on(this.clickEvent, this.onClick, this);
6864         if(this.tooltip){
6865             if(typeof this.tooltip == 'object'){
6866                 Roo.QuickTips.tips(Roo.apply({
6867                       target: btnEl.id
6868                 }, this.tooltip));
6869             } else {
6870                 btnEl.dom[this.tooltipType] = this.tooltip;
6871             }
6872         }
6873         if(this.arrowTooltip){
6874             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
6875         }
6876         if(this.hidden){
6877             this.hide();
6878         }
6879         if(this.disabled){
6880             this.disable();
6881         }
6882         if(this.pressed){
6883             this.el.addClass("x-btn-pressed");
6884         }
6885         if(Roo.isIE && !Roo.isIE7){
6886             this.autoWidth.defer(1, this);
6887         }else{
6888             this.autoWidth();
6889         }
6890         if(this.menu){
6891             this.menu.on("show", this.onMenuShow, this);
6892             this.menu.on("hide", this.onMenuHide, this);
6893         }
6894         this.fireEvent('render', this);
6895     },
6896
6897     // private
6898     autoWidth : function(){
6899         if(this.el){
6900             var tbl = this.el.child("table:first");
6901             var tbl2 = this.el.child("table:last");
6902             this.el.setWidth("auto");
6903             tbl.setWidth("auto");
6904             if(Roo.isIE7 && Roo.isStrict){
6905                 var ib = this.el.child('button:first');
6906                 if(ib && ib.getWidth() > 20){
6907                     ib.clip();
6908                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6909                 }
6910             }
6911             if(this.minWidth){
6912                 if(this.hidden){
6913                     this.el.beginMeasure();
6914                 }
6915                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
6916                     tbl.setWidth(this.minWidth-tbl2.getWidth());
6917                 }
6918                 if(this.hidden){
6919                     this.el.endMeasure();
6920                 }
6921             }
6922             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
6923         } 
6924     },
6925     /**
6926      * Sets this button's click handler
6927      * @param {Function} handler The function to call when the button is clicked
6928      * @param {Object} scope (optional) Scope for the function passed above
6929      */
6930     setHandler : function(handler, scope){
6931         this.handler = handler;
6932         this.scope = scope;  
6933     },
6934     
6935     /**
6936      * Sets this button's arrow click handler
6937      * @param {Function} handler The function to call when the arrow is clicked
6938      * @param {Object} scope (optional) Scope for the function passed above
6939      */
6940     setArrowHandler : function(handler, scope){
6941         this.arrowHandler = handler;
6942         this.scope = scope;  
6943     },
6944     
6945     /**
6946      * Focus the button
6947      */
6948     focus : function(){
6949         if(this.el){
6950             this.el.child("button:first").focus();
6951         }
6952     },
6953
6954     // private
6955     onClick : function(e){
6956         e.preventDefault();
6957         if(!this.disabled){
6958             if(e.getTarget(".x-btn-menu-arrow-wrap")){
6959                 if(this.menu && !this.menu.isVisible()){
6960                     this.menu.show(this.el, this.menuAlign);
6961                 }
6962                 this.fireEvent("arrowclick", this, e);
6963                 if(this.arrowHandler){
6964                     this.arrowHandler.call(this.scope || this, this, e);
6965                 }
6966             }else{
6967                 this.fireEvent("click", this, e);
6968                 if(this.handler){
6969                     this.handler.call(this.scope || this, this, e);
6970                 }
6971             }
6972         }
6973     },
6974     // private
6975     onMouseDown : function(e){
6976         if(!this.disabled){
6977             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
6978         }
6979     },
6980     // private
6981     onMouseUp : function(e){
6982         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
6983     }   
6984 });
6985
6986
6987 // backwards compat
6988 Roo.MenuButton = Roo.SplitButton;/*
6989  * Based on:
6990  * Ext JS Library 1.1.1
6991  * Copyright(c) 2006-2007, Ext JS, LLC.
6992  *
6993  * Originally Released Under LGPL - original licence link has changed is not relivant.
6994  *
6995  * Fork - LGPL
6996  * <script type="text/javascript">
6997  */
6998
6999 /**
7000  * @class Roo.Toolbar
7001  * @children   Roo.Toolbar.Item Roo.form.Field
7002  * Basic Toolbar class.
7003  * @constructor
7004  * Creates a new Toolbar
7005  * @param {Object} container The config object
7006  */ 
7007 Roo.Toolbar = function(container, buttons, config)
7008 {
7009     /// old consturctor format still supported..
7010     if(container instanceof Array){ // omit the container for later rendering
7011         buttons = container;
7012         config = buttons;
7013         container = null;
7014     }
7015     if (typeof(container) == 'object' && container.xtype) {
7016         config = container;
7017         container = config.container;
7018         buttons = config.buttons || []; // not really - use items!!
7019     }
7020     var xitems = [];
7021     if (config && config.items) {
7022         xitems = config.items;
7023         delete config.items;
7024     }
7025     Roo.apply(this, config);
7026     this.buttons = buttons;
7027     
7028     if(container){
7029         this.render(container);
7030     }
7031     this.xitems = xitems;
7032     Roo.each(xitems, function(b) {
7033         this.add(b);
7034     }, this);
7035     
7036 };
7037
7038 Roo.Toolbar.prototype = {
7039     /**
7040      * @cfg {Array} items
7041      * array of button configs or elements to add (will be converted to a MixedCollection)
7042      */
7043     items: false,
7044     /**
7045      * @cfg {String/HTMLElement/Element} container
7046      * The id or element that will contain the toolbar
7047      */
7048     // private
7049     render : function(ct){
7050         this.el = Roo.get(ct);
7051         if(this.cls){
7052             this.el.addClass(this.cls);
7053         }
7054         // using a table allows for vertical alignment
7055         // 100% width is needed by Safari...
7056         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7057         this.tr = this.el.child("tr", true);
7058         var autoId = 0;
7059         this.items = new Roo.util.MixedCollection(false, function(o){
7060             return o.id || ("item" + (++autoId));
7061         });
7062         if(this.buttons){
7063             this.add.apply(this, this.buttons);
7064             delete this.buttons;
7065         }
7066     },
7067
7068     /**
7069      * Adds element(s) to the toolbar -- this function takes a variable number of 
7070      * arguments of mixed type and adds them to the toolbar.
7071      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7072      * <ul>
7073      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7074      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7075      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7076      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7077      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7078      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7079      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7080      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7081      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7082      * </ul>
7083      * @param {Mixed} arg2
7084      * @param {Mixed} etc.
7085      */
7086     add : function(){
7087         var a = arguments, l = a.length;
7088         for(var i = 0; i < l; i++){
7089             this._add(a[i]);
7090         }
7091     },
7092     // private..
7093     _add : function(el) {
7094         
7095         if (el.xtype) {
7096             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7097         }
7098         
7099         if (el.applyTo){ // some kind of form field
7100             return this.addField(el);
7101         } 
7102         if (el.render){ // some kind of Toolbar.Item
7103             return this.addItem(el);
7104         }
7105         if (typeof el == "string"){ // string
7106             if(el == "separator" || el == "-"){
7107                 return this.addSeparator();
7108             }
7109             if (el == " "){
7110                 return this.addSpacer();
7111             }
7112             if(el == "->"){
7113                 return this.addFill();
7114             }
7115             return this.addText(el);
7116             
7117         }
7118         if(el.tagName){ // element
7119             return this.addElement(el);
7120         }
7121         if(typeof el == "object"){ // must be button config?
7122             return this.addButton(el);
7123         }
7124         // and now what?!?!
7125         return false;
7126         
7127     },
7128     
7129     /**
7130      * Add an Xtype element
7131      * @param {Object} xtype Xtype Object
7132      * @return {Object} created Object
7133      */
7134     addxtype : function(e){
7135         return this.add(e);  
7136     },
7137     
7138     /**
7139      * Returns the Element for this toolbar.
7140      * @return {Roo.Element}
7141      */
7142     getEl : function(){
7143         return this.el;  
7144     },
7145     
7146     /**
7147      * Adds a separator
7148      * @return {Roo.Toolbar.Item} The separator item
7149      */
7150     addSeparator : function(){
7151         return this.addItem(new Roo.Toolbar.Separator());
7152     },
7153
7154     /**
7155      * Adds a spacer element
7156      * @return {Roo.Toolbar.Spacer} The spacer item
7157      */
7158     addSpacer : function(){
7159         return this.addItem(new Roo.Toolbar.Spacer());
7160     },
7161
7162     /**
7163      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7164      * @return {Roo.Toolbar.Fill} The fill item
7165      */
7166     addFill : function(){
7167         return this.addItem(new Roo.Toolbar.Fill());
7168     },
7169
7170     /**
7171      * Adds any standard HTML element to the toolbar
7172      * @param {String/HTMLElement/Element} el The element or id of the element to add
7173      * @return {Roo.Toolbar.Item} The element's item
7174      */
7175     addElement : function(el){
7176         return this.addItem(new Roo.Toolbar.Item(el));
7177     },
7178     /**
7179      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7180      * @type Roo.util.MixedCollection  
7181      */
7182     items : false,
7183      
7184     /**
7185      * Adds any Toolbar.Item or subclass
7186      * @param {Roo.Toolbar.Item} item
7187      * @return {Roo.Toolbar.Item} The item
7188      */
7189     addItem : function(item){
7190         var td = this.nextBlock();
7191         item.render(td);
7192         this.items.add(item);
7193         return item;
7194     },
7195     
7196     /**
7197      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7198      * @param {Object/Array} config A button config or array of configs
7199      * @return {Roo.Toolbar.Button/Array}
7200      */
7201     addButton : function(config){
7202         if(config instanceof Array){
7203             var buttons = [];
7204             for(var i = 0, len = config.length; i < len; i++) {
7205                 buttons.push(this.addButton(config[i]));
7206             }
7207             return buttons;
7208         }
7209         var b = config;
7210         if(!(config instanceof Roo.Toolbar.Button)){
7211             b = config.split ?
7212                 new Roo.Toolbar.SplitButton(config) :
7213                 new Roo.Toolbar.Button(config);
7214         }
7215         var td = this.nextBlock();
7216         b.render(td);
7217         this.items.add(b);
7218         return b;
7219     },
7220     
7221     /**
7222      * Adds text to the toolbar
7223      * @param {String} text The text to add
7224      * @return {Roo.Toolbar.Item} The element's item
7225      */
7226     addText : function(text){
7227         return this.addItem(new Roo.Toolbar.TextItem(text));
7228     },
7229     
7230     /**
7231      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7232      * @param {Number} index The index where the item is to be inserted
7233      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7234      * @return {Roo.Toolbar.Button/Item}
7235      */
7236     insertButton : function(index, item){
7237         if(item instanceof Array){
7238             var buttons = [];
7239             for(var i = 0, len = item.length; i < len; i++) {
7240                buttons.push(this.insertButton(index + i, item[i]));
7241             }
7242             return buttons;
7243         }
7244         if (!(item instanceof Roo.Toolbar.Button)){
7245            item = new Roo.Toolbar.Button(item);
7246         }
7247         var td = document.createElement("td");
7248         this.tr.insertBefore(td, this.tr.childNodes[index]);
7249         item.render(td);
7250         this.items.insert(index, item);
7251         return item;
7252     },
7253     
7254     /**
7255      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7256      * @param {Object} config
7257      * @return {Roo.Toolbar.Item} The element's item
7258      */
7259     addDom : function(config, returnEl){
7260         var td = this.nextBlock();
7261         Roo.DomHelper.overwrite(td, config);
7262         var ti = new Roo.Toolbar.Item(td.firstChild);
7263         ti.render(td);
7264         this.items.add(ti);
7265         return ti;
7266     },
7267
7268     /**
7269      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7270      * @type Roo.util.MixedCollection  
7271      */
7272     fields : false,
7273     
7274     /**
7275      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7276      * Note: the field should not have been rendered yet. For a field that has already been
7277      * rendered, use {@link #addElement}.
7278      * @param {Roo.form.Field} field
7279      * @return {Roo.ToolbarItem}
7280      */
7281      
7282       
7283     addField : function(field) {
7284         if (!this.fields) {
7285             var autoId = 0;
7286             this.fields = new Roo.util.MixedCollection(false, function(o){
7287                 return o.id || ("item" + (++autoId));
7288             });
7289
7290         }
7291         
7292         var td = this.nextBlock();
7293         field.render(td);
7294         var ti = new Roo.Toolbar.Item(td.firstChild);
7295         ti.render(td);
7296         this.items.add(ti);
7297         this.fields.add(field);
7298         return ti;
7299     },
7300     /**
7301      * Hide the toolbar
7302      * @method hide
7303      */
7304      
7305       
7306     hide : function()
7307     {
7308         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7309         this.el.child('div').hide();
7310     },
7311     /**
7312      * Show the toolbar
7313      * @method show
7314      */
7315     show : function()
7316     {
7317         this.el.child('div').show();
7318     },
7319       
7320     // private
7321     nextBlock : function(){
7322         var td = document.createElement("td");
7323         this.tr.appendChild(td);
7324         return td;
7325     },
7326
7327     // private
7328     destroy : function(){
7329         if(this.items){ // rendered?
7330             Roo.destroy.apply(Roo, this.items.items);
7331         }
7332         if(this.fields){ // rendered?
7333             Roo.destroy.apply(Roo, this.fields.items);
7334         }
7335         Roo.Element.uncache(this.el, this.tr);
7336     }
7337 };
7338
7339 /**
7340  * @class Roo.Toolbar.Item
7341  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7342  * @constructor
7343  * Creates a new Item
7344  * @param {HTMLElement} el 
7345  */
7346 Roo.Toolbar.Item = function(el){
7347     var cfg = {};
7348     if (typeof (el.xtype) != 'undefined') {
7349         cfg = el;
7350         el = cfg.el;
7351     }
7352     
7353     this.el = Roo.getDom(el);
7354     this.id = Roo.id(this.el);
7355     this.hidden = false;
7356     
7357     this.addEvents({
7358          /**
7359              * @event render
7360              * Fires when the button is rendered
7361              * @param {Button} this
7362              */
7363         'render': true
7364     });
7365     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7366 };
7367 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7368 //Roo.Toolbar.Item.prototype = {
7369     
7370     /**
7371      * Get this item's HTML Element
7372      * @return {HTMLElement}
7373      */
7374     getEl : function(){
7375        return this.el;  
7376     },
7377
7378     // private
7379     render : function(td){
7380         
7381          this.td = td;
7382         td.appendChild(this.el);
7383         
7384         this.fireEvent('render', this);
7385     },
7386     
7387     /**
7388      * Removes and destroys this item.
7389      */
7390     destroy : function(){
7391         this.td.parentNode.removeChild(this.td);
7392     },
7393     
7394     /**
7395      * Shows this item.
7396      */
7397     show: function(){
7398         this.hidden = false;
7399         this.td.style.display = "";
7400     },
7401     
7402     /**
7403      * Hides this item.
7404      */
7405     hide: function(){
7406         this.hidden = true;
7407         this.td.style.display = "none";
7408     },
7409     
7410     /**
7411      * Convenience function for boolean show/hide.
7412      * @param {Boolean} visible true to show/false to hide
7413      */
7414     setVisible: function(visible){
7415         if(visible) {
7416             this.show();
7417         }else{
7418             this.hide();
7419         }
7420     },
7421     
7422     /**
7423      * Try to focus this item.
7424      */
7425     focus : function(){
7426         Roo.fly(this.el).focus();
7427     },
7428     
7429     /**
7430      * Disables this item.
7431      */
7432     disable : function(){
7433         Roo.fly(this.td).addClass("x-item-disabled");
7434         this.disabled = true;
7435         this.el.disabled = true;
7436     },
7437     
7438     /**
7439      * Enables this item.
7440      */
7441     enable : function(){
7442         Roo.fly(this.td).removeClass("x-item-disabled");
7443         this.disabled = false;
7444         this.el.disabled = false;
7445     }
7446 });
7447
7448
7449 /**
7450  * @class Roo.Toolbar.Separator
7451  * @extends Roo.Toolbar.Item
7452  * A simple toolbar separator class
7453  * @constructor
7454  * Creates a new Separator
7455  */
7456 Roo.Toolbar.Separator = function(cfg){
7457     
7458     var s = document.createElement("span");
7459     s.className = "ytb-sep";
7460     if (cfg) {
7461         cfg.el = s;
7462     }
7463     
7464     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7465 };
7466 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7467     enable:Roo.emptyFn,
7468     disable:Roo.emptyFn,
7469     focus:Roo.emptyFn
7470 });
7471
7472 /**
7473  * @class Roo.Toolbar.Spacer
7474  * @extends Roo.Toolbar.Item
7475  * A simple element that adds extra horizontal space to a toolbar.
7476  * @constructor
7477  * Creates a new Spacer
7478  */
7479 Roo.Toolbar.Spacer = function(cfg){
7480     var s = document.createElement("div");
7481     s.className = "ytb-spacer";
7482     if (cfg) {
7483         cfg.el = s;
7484     }
7485     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7486 };
7487 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7488     enable:Roo.emptyFn,
7489     disable:Roo.emptyFn,
7490     focus:Roo.emptyFn
7491 });
7492
7493 /**
7494  * @class Roo.Toolbar.Fill
7495  * @extends Roo.Toolbar.Spacer
7496  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7497  * @constructor
7498  * Creates a new Spacer
7499  */
7500 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7501     // private
7502     render : function(td){
7503         td.style.width = '100%';
7504         Roo.Toolbar.Fill.superclass.render.call(this, td);
7505     }
7506 });
7507
7508 /**
7509  * @class Roo.Toolbar.TextItem
7510  * @extends Roo.Toolbar.Item
7511  * A simple class that renders text directly into a toolbar.
7512  * @constructor
7513  * Creates a new TextItem
7514  * @cfg {string} text 
7515  */
7516 Roo.Toolbar.TextItem = function(cfg){
7517     var  text = cfg || "";
7518     if (typeof(cfg) == 'object') {
7519         text = cfg.text || "";
7520     }  else {
7521         cfg = null;
7522     }
7523     var s = document.createElement("span");
7524     s.className = "ytb-text";
7525     s.innerHTML = text;
7526     if (cfg) {
7527         cfg.el  = s;
7528     }
7529     
7530     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7531 };
7532 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7533     
7534      
7535     enable:Roo.emptyFn,
7536     disable:Roo.emptyFn,
7537     focus:Roo.emptyFn
7538 });
7539
7540 /**
7541  * @class Roo.Toolbar.Button
7542  * @extends Roo.Button
7543  * A button that renders into a toolbar.
7544  * @constructor
7545  * Creates a new Button
7546  * @param {Object} config A standard {@link Roo.Button} config object
7547  */
7548 Roo.Toolbar.Button = function(config){
7549     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7550 };
7551 Roo.extend(Roo.Toolbar.Button, Roo.Button,
7552 {
7553     
7554     
7555     render : function(td){
7556         this.td = td;
7557         Roo.Toolbar.Button.superclass.render.call(this, td);
7558     },
7559     
7560     /**
7561      * Removes and destroys this button
7562      */
7563     destroy : function(){
7564         Roo.Toolbar.Button.superclass.destroy.call(this);
7565         this.td.parentNode.removeChild(this.td);
7566     },
7567     
7568     /**
7569      * Shows this button
7570      */
7571     show: function(){
7572         this.hidden = false;
7573         this.td.style.display = "";
7574     },
7575     
7576     /**
7577      * Hides this button
7578      */
7579     hide: function(){
7580         this.hidden = true;
7581         this.td.style.display = "none";
7582     },
7583
7584     /**
7585      * Disables this item
7586      */
7587     disable : function(){
7588         Roo.fly(this.td).addClass("x-item-disabled");
7589         this.disabled = true;
7590     },
7591
7592     /**
7593      * Enables this item
7594      */
7595     enable : function(){
7596         Roo.fly(this.td).removeClass("x-item-disabled");
7597         this.disabled = false;
7598     }
7599 });
7600 // backwards compat
7601 Roo.ToolbarButton = Roo.Toolbar.Button;
7602
7603 /**
7604  * @class Roo.Toolbar.SplitButton
7605  * @extends Roo.SplitButton
7606  * A menu button that renders into a toolbar.
7607  * @constructor
7608  * Creates a new SplitButton
7609  * @param {Object} config A standard {@link Roo.SplitButton} config object
7610  */
7611 Roo.Toolbar.SplitButton = function(config){
7612     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
7613 };
7614 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
7615     render : function(td){
7616         this.td = td;
7617         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
7618     },
7619     
7620     /**
7621      * Removes and destroys this button
7622      */
7623     destroy : function(){
7624         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
7625         this.td.parentNode.removeChild(this.td);
7626     },
7627     
7628     /**
7629      * Shows this button
7630      */
7631     show: function(){
7632         this.hidden = false;
7633         this.td.style.display = "";
7634     },
7635     
7636     /**
7637      * Hides this button
7638      */
7639     hide: function(){
7640         this.hidden = true;
7641         this.td.style.display = "none";
7642     }
7643 });
7644
7645 // backwards compat
7646 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
7647  * Based on:
7648  * Ext JS Library 1.1.1
7649  * Copyright(c) 2006-2007, Ext JS, LLC.
7650  *
7651  * Originally Released Under LGPL - original licence link has changed is not relivant.
7652  *
7653  * Fork - LGPL
7654  * <script type="text/javascript">
7655  */
7656  
7657 /**
7658  * @class Roo.PagingToolbar
7659  * @extends Roo.Toolbar
7660  * @children   Roo.Toolbar.Item Roo.form.Field
7661  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
7662  * @constructor
7663  * Create a new PagingToolbar
7664  * @param {Object} config The config object
7665  */
7666 Roo.PagingToolbar = function(el, ds, config)
7667 {
7668     // old args format still supported... - xtype is prefered..
7669     if (typeof(el) == 'object' && el.xtype) {
7670         // created from xtype...
7671         config = el;
7672         ds = el.dataSource;
7673         el = config.container;
7674     }
7675     var items = [];
7676     if (config.items) {
7677         items = config.items;
7678         config.items = [];
7679     }
7680     
7681     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
7682     this.ds = ds;
7683     this.cursor = 0;
7684     this.renderButtons(this.el);
7685     this.bind(ds);
7686     
7687     // supprot items array.
7688    
7689     Roo.each(items, function(e) {
7690         this.add(Roo.factory(e));
7691     },this);
7692     
7693 };
7694
7695 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
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     
7707     /**
7708      * @cfg {Number} pageSize
7709      * The number of records to display per page (defaults to 20)
7710      */
7711     pageSize: 20,
7712     /**
7713      * @cfg {String} displayMsg
7714      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
7715      */
7716     displayMsg : 'Displaying {0} - {1} of {2}',
7717     /**
7718      * @cfg {String} emptyMsg
7719      * The message to display when no records are found (defaults to "No data to display")
7720      */
7721     emptyMsg : 'No data to display',
7722     /**
7723      * Customizable piece of the default paging text (defaults to "Page")
7724      * @type String
7725      */
7726     beforePageText : "Page",
7727     /**
7728      * Customizable piece of the default paging text (defaults to "of %0")
7729      * @type String
7730      */
7731     afterPageText : "of {0}",
7732     /**
7733      * Customizable piece of the default paging text (defaults to "First Page")
7734      * @type String
7735      */
7736     firstText : "First Page",
7737     /**
7738      * Customizable piece of the default paging text (defaults to "Previous Page")
7739      * @type String
7740      */
7741     prevText : "Previous Page",
7742     /**
7743      * Customizable piece of the default paging text (defaults to "Next Page")
7744      * @type String
7745      */
7746     nextText : "Next Page",
7747     /**
7748      * Customizable piece of the default paging text (defaults to "Last Page")
7749      * @type String
7750      */
7751     lastText : "Last Page",
7752     /**
7753      * Customizable piece of the default paging text (defaults to "Refresh")
7754      * @type String
7755      */
7756     refreshText : "Refresh",
7757
7758     // private
7759     renderButtons : function(el){
7760         Roo.PagingToolbar.superclass.render.call(this, el);
7761         this.first = this.addButton({
7762             tooltip: this.firstText,
7763             cls: "x-btn-icon x-grid-page-first",
7764             disabled: true,
7765             handler: this.onClick.createDelegate(this, ["first"])
7766         });
7767         this.prev = this.addButton({
7768             tooltip: this.prevText,
7769             cls: "x-btn-icon x-grid-page-prev",
7770             disabled: true,
7771             handler: this.onClick.createDelegate(this, ["prev"])
7772         });
7773         //this.addSeparator();
7774         this.add(this.beforePageText);
7775         this.field = Roo.get(this.addDom({
7776            tag: "input",
7777            type: "text",
7778            size: "3",
7779            value: "1",
7780            cls: "x-grid-page-number"
7781         }).el);
7782         this.field.on("keydown", this.onPagingKeydown, this);
7783         this.field.on("focus", function(){this.dom.select();});
7784         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
7785         this.field.setHeight(18);
7786         //this.addSeparator();
7787         this.next = this.addButton({
7788             tooltip: this.nextText,
7789             cls: "x-btn-icon x-grid-page-next",
7790             disabled: true,
7791             handler: this.onClick.createDelegate(this, ["next"])
7792         });
7793         this.last = this.addButton({
7794             tooltip: this.lastText,
7795             cls: "x-btn-icon x-grid-page-last",
7796             disabled: true,
7797             handler: this.onClick.createDelegate(this, ["last"])
7798         });
7799         //this.addSeparator();
7800         this.loading = this.addButton({
7801             tooltip: this.refreshText,
7802             cls: "x-btn-icon x-grid-loading",
7803             handler: this.onClick.createDelegate(this, ["refresh"])
7804         });
7805
7806         if(this.displayInfo){
7807             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
7808         }
7809     },
7810
7811     // private
7812     updateInfo : function(){
7813         if(this.displayEl){
7814             var count = this.ds.getCount();
7815             var msg = count == 0 ?
7816                 this.emptyMsg :
7817                 String.format(
7818                     this.displayMsg,
7819                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
7820                 );
7821             this.displayEl.update(msg);
7822         }
7823     },
7824
7825     // private
7826     onLoad : function(ds, r, o){
7827        this.cursor = o.params ? o.params.start : 0;
7828        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
7829
7830        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
7831        this.field.dom.value = ap;
7832        this.first.setDisabled(ap == 1);
7833        this.prev.setDisabled(ap == 1);
7834        this.next.setDisabled(ap == ps);
7835        this.last.setDisabled(ap == ps);
7836        this.loading.enable();
7837        this.updateInfo();
7838     },
7839
7840     // private
7841     getPageData : function(){
7842         var total = this.ds.getTotalCount();
7843         return {
7844             total : total,
7845             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
7846             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
7847         };
7848     },
7849
7850     // private
7851     onLoadError : function(){
7852         this.loading.enable();
7853     },
7854
7855     // private
7856     onPagingKeydown : function(e){
7857         var k = e.getKey();
7858         var d = this.getPageData();
7859         if(k == e.RETURN){
7860             var v = this.field.dom.value, pageNum;
7861             if(!v || isNaN(pageNum = parseInt(v, 10))){
7862                 this.field.dom.value = d.activePage;
7863                 return;
7864             }
7865             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
7866             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7867             e.stopEvent();
7868         }
7869         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))
7870         {
7871           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
7872           this.field.dom.value = pageNum;
7873           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
7874           e.stopEvent();
7875         }
7876         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
7877         {
7878           var v = this.field.dom.value, pageNum; 
7879           var increment = (e.shiftKey) ? 10 : 1;
7880           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
7881             increment *= -1;
7882           }
7883           if(!v || isNaN(pageNum = parseInt(v, 10))) {
7884             this.field.dom.value = d.activePage;
7885             return;
7886           }
7887           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
7888           {
7889             this.field.dom.value = parseInt(v, 10) + increment;
7890             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
7891             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7892           }
7893           e.stopEvent();
7894         }
7895     },
7896
7897     // private
7898     beforeLoad : function(){
7899         if(this.loading){
7900             this.loading.disable();
7901         }
7902     },
7903
7904     // private
7905     onClick : function(which){
7906         var ds = this.ds;
7907         switch(which){
7908             case "first":
7909                 ds.load({params:{start: 0, limit: this.pageSize}});
7910             break;
7911             case "prev":
7912                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
7913             break;
7914             case "next":
7915                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
7916             break;
7917             case "last":
7918                 var total = ds.getTotalCount();
7919                 var extra = total % this.pageSize;
7920                 var lastStart = extra ? (total - extra) : total-this.pageSize;
7921                 ds.load({params:{start: lastStart, limit: this.pageSize}});
7922             break;
7923             case "refresh":
7924                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
7925             break;
7926         }
7927     },
7928
7929     /**
7930      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
7931      * @param {Roo.data.Store} store The data store to unbind
7932      */
7933     unbind : function(ds){
7934         ds.un("beforeload", this.beforeLoad, this);
7935         ds.un("load", this.onLoad, this);
7936         ds.un("loadexception", this.onLoadError, this);
7937         ds.un("remove", this.updateInfo, this);
7938         ds.un("add", this.updateInfo, this);
7939         this.ds = undefined;
7940     },
7941
7942     /**
7943      * Binds the paging toolbar to the specified {@link Roo.data.Store}
7944      * @param {Roo.data.Store} store The data store to bind
7945      */
7946     bind : function(ds){
7947         ds.on("beforeload", this.beforeLoad, this);
7948         ds.on("load", this.onLoad, this);
7949         ds.on("loadexception", this.onLoadError, this);
7950         ds.on("remove", this.updateInfo, this);
7951         ds.on("add", this.updateInfo, this);
7952         this.ds = ds;
7953     }
7954 });/*
7955  * Based on:
7956  * Ext JS Library 1.1.1
7957  * Copyright(c) 2006-2007, Ext JS, LLC.
7958  *
7959  * Originally Released Under LGPL - original licence link has changed is not relivant.
7960  *
7961  * Fork - LGPL
7962  * <script type="text/javascript">
7963  */
7964
7965 /**
7966  * @class Roo.Resizable
7967  * @extends Roo.util.Observable
7968  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
7969  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
7970  * 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
7971  * the element will be wrapped for you automatically.</p>
7972  * <p>Here is the list of valid resize handles:</p>
7973  * <pre>
7974 Value   Description
7975 ------  -------------------
7976  'n'     north
7977  's'     south
7978  'e'     east
7979  'w'     west
7980  'nw'    northwest
7981  'sw'    southwest
7982  'se'    southeast
7983  'ne'    northeast
7984  'hd'    horizontal drag
7985  'all'   all
7986 </pre>
7987  * <p>Here's an example showing the creation of a typical Resizable:</p>
7988  * <pre><code>
7989 var resizer = new Roo.Resizable("element-id", {
7990     handles: 'all',
7991     minWidth: 200,
7992     minHeight: 100,
7993     maxWidth: 500,
7994     maxHeight: 400,
7995     pinned: true
7996 });
7997 resizer.on("resize", myHandler);
7998 </code></pre>
7999  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8000  * resizer.east.setDisplayed(false);</p>
8001  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8002  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8003  * resize operation's new size (defaults to [0, 0])
8004  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8005  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8006  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8007  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8008  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8009  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8010  * @cfg {Number} width The width of the element in pixels (defaults to null)
8011  * @cfg {Number} height The height of the element in pixels (defaults to null)
8012  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8013  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8014  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8015  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8016  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8017  * in favor of the handles config option (defaults to false)
8018  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8019  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8020  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8021  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8022  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8023  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8024  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8025  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8026  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8027  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8028  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8029  * @constructor
8030  * Create a new resizable component
8031  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8032  * @param {Object} config configuration options
8033   */
8034 Roo.Resizable = function(el, config)
8035 {
8036     this.el = Roo.get(el);
8037
8038     if(config && config.wrap){
8039         config.resizeChild = this.el;
8040         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8041         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8042         this.el.setStyle("overflow", "hidden");
8043         this.el.setPositioning(config.resizeChild.getPositioning());
8044         config.resizeChild.clearPositioning();
8045         if(!config.width || !config.height){
8046             var csize = config.resizeChild.getSize();
8047             this.el.setSize(csize.width, csize.height);
8048         }
8049         if(config.pinned && !config.adjustments){
8050             config.adjustments = "auto";
8051         }
8052     }
8053
8054     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8055     this.proxy.unselectable();
8056     this.proxy.enableDisplayMode('block');
8057
8058     Roo.apply(this, config);
8059
8060     if(this.pinned){
8061         this.disableTrackOver = true;
8062         this.el.addClass("x-resizable-pinned");
8063     }
8064     // if the element isn't positioned, make it relative
8065     var position = this.el.getStyle("position");
8066     if(position != "absolute" && position != "fixed"){
8067         this.el.setStyle("position", "relative");
8068     }
8069     if(!this.handles){ // no handles passed, must be legacy style
8070         this.handles = 's,e,se';
8071         if(this.multiDirectional){
8072             this.handles += ',n,w';
8073         }
8074     }
8075     if(this.handles == "all"){
8076         this.handles = "n s e w ne nw se sw";
8077     }
8078     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8079     var ps = Roo.Resizable.positions;
8080     for(var i = 0, len = hs.length; i < len; i++){
8081         if(hs[i] && ps[hs[i]]){
8082             var pos = ps[hs[i]];
8083             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8084         }
8085     }
8086     // legacy
8087     this.corner = this.southeast;
8088     
8089     // updateBox = the box can move..
8090     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8091         this.updateBox = true;
8092     }
8093
8094     this.activeHandle = null;
8095
8096     if(this.resizeChild){
8097         if(typeof this.resizeChild == "boolean"){
8098             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8099         }else{
8100             this.resizeChild = Roo.get(this.resizeChild, true);
8101         }
8102     }
8103     
8104     if(this.adjustments == "auto"){
8105         var rc = this.resizeChild;
8106         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8107         if(rc && (hw || hn)){
8108             rc.position("relative");
8109             rc.setLeft(hw ? hw.el.getWidth() : 0);
8110             rc.setTop(hn ? hn.el.getHeight() : 0);
8111         }
8112         this.adjustments = [
8113             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8114             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8115         ];
8116     }
8117
8118     if(this.draggable){
8119         this.dd = this.dynamic ?
8120             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8121         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8122     }
8123
8124     // public events
8125     this.addEvents({
8126         /**
8127          * @event beforeresize
8128          * Fired before resize is allowed. Set enabled to false to cancel resize.
8129          * @param {Roo.Resizable} this
8130          * @param {Roo.EventObject} e The mousedown event
8131          */
8132         "beforeresize" : true,
8133         /**
8134          * @event resizing
8135          * Fired a resizing.
8136          * @param {Roo.Resizable} this
8137          * @param {Number} x The new x position
8138          * @param {Number} y The new y position
8139          * @param {Number} w The new w width
8140          * @param {Number} h The new h hight
8141          * @param {Roo.EventObject} e The mouseup event
8142          */
8143         "resizing" : true,
8144         /**
8145          * @event resize
8146          * Fired after a resize.
8147          * @param {Roo.Resizable} this
8148          * @param {Number} width The new width
8149          * @param {Number} height The new height
8150          * @param {Roo.EventObject} e The mouseup event
8151          */
8152         "resize" : true
8153     });
8154
8155     if(this.width !== null && this.height !== null){
8156         this.resizeTo(this.width, this.height);
8157     }else{
8158         this.updateChildSize();
8159     }
8160     if(Roo.isIE){
8161         this.el.dom.style.zoom = 1;
8162     }
8163     Roo.Resizable.superclass.constructor.call(this);
8164 };
8165
8166 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8167         resizeChild : false,
8168         adjustments : [0, 0],
8169         minWidth : 5,
8170         minHeight : 5,
8171         maxWidth : 10000,
8172         maxHeight : 10000,
8173         enabled : true,
8174         animate : false,
8175         duration : .35,
8176         dynamic : false,
8177         handles : false,
8178         multiDirectional : false,
8179         disableTrackOver : false,
8180         easing : 'easeOutStrong',
8181         widthIncrement : 0,
8182         heightIncrement : 0,
8183         pinned : false,
8184         width : null,
8185         height : null,
8186         preserveRatio : false,
8187         transparent: false,
8188         minX: 0,
8189         minY: 0,
8190         draggable: false,
8191
8192         /**
8193          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8194          */
8195         constrainTo: undefined,
8196         /**
8197          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8198          */
8199         resizeRegion: undefined,
8200
8201
8202     /**
8203      * Perform a manual resize
8204      * @param {Number} width
8205      * @param {Number} height
8206      */
8207     resizeTo : function(width, height){
8208         this.el.setSize(width, height);
8209         this.updateChildSize();
8210         this.fireEvent("resize", this, width, height, null);
8211     },
8212
8213     // private
8214     startSizing : function(e, handle){
8215         this.fireEvent("beforeresize", this, e);
8216         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8217
8218             if(!this.overlay){
8219                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8220                 this.overlay.unselectable();
8221                 this.overlay.enableDisplayMode("block");
8222                 this.overlay.on("mousemove", this.onMouseMove, this);
8223                 this.overlay.on("mouseup", this.onMouseUp, this);
8224             }
8225             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8226
8227             this.resizing = true;
8228             this.startBox = this.el.getBox();
8229             this.startPoint = e.getXY();
8230             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8231                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8232
8233             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8234             this.overlay.show();
8235
8236             if(this.constrainTo) {
8237                 var ct = Roo.get(this.constrainTo);
8238                 this.resizeRegion = ct.getRegion().adjust(
8239                     ct.getFrameWidth('t'),
8240                     ct.getFrameWidth('l'),
8241                     -ct.getFrameWidth('b'),
8242                     -ct.getFrameWidth('r')
8243                 );
8244             }
8245
8246             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8247             this.proxy.show();
8248             this.proxy.setBox(this.startBox);
8249             if(!this.dynamic){
8250                 this.proxy.setStyle('visibility', 'visible');
8251             }
8252         }
8253     },
8254
8255     // private
8256     onMouseDown : function(handle, e){
8257         if(this.enabled){
8258             e.stopEvent();
8259             this.activeHandle = handle;
8260             this.startSizing(e, handle);
8261         }
8262     },
8263
8264     // private
8265     onMouseUp : function(e){
8266         var size = this.resizeElement();
8267         this.resizing = false;
8268         this.handleOut();
8269         this.overlay.hide();
8270         this.proxy.hide();
8271         this.fireEvent("resize", this, size.width, size.height, e);
8272     },
8273
8274     // private
8275     updateChildSize : function(){
8276         
8277         if(this.resizeChild){
8278             var el = this.el;
8279             var child = this.resizeChild;
8280             var adj = this.adjustments;
8281             if(el.dom.offsetWidth){
8282                 var b = el.getSize(true);
8283                 child.setSize(b.width+adj[0], b.height+adj[1]);
8284             }
8285             // Second call here for IE
8286             // The first call enables instant resizing and
8287             // the second call corrects scroll bars if they
8288             // exist
8289             if(Roo.isIE){
8290                 setTimeout(function(){
8291                     if(el.dom.offsetWidth){
8292                         var b = el.getSize(true);
8293                         child.setSize(b.width+adj[0], b.height+adj[1]);
8294                     }
8295                 }, 10);
8296             }
8297         }
8298     },
8299
8300     // private
8301     snap : function(value, inc, min){
8302         if(!inc || !value) {
8303             return value;
8304         }
8305         var newValue = value;
8306         var m = value % inc;
8307         if(m > 0){
8308             if(m > (inc/2)){
8309                 newValue = value + (inc-m);
8310             }else{
8311                 newValue = value - m;
8312             }
8313         }
8314         return Math.max(min, newValue);
8315     },
8316
8317     // private
8318     resizeElement : function(){
8319         var box = this.proxy.getBox();
8320         if(this.updateBox){
8321             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8322         }else{
8323             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8324         }
8325         this.updateChildSize();
8326         if(!this.dynamic){
8327             this.proxy.hide();
8328         }
8329         return box;
8330     },
8331
8332     // private
8333     constrain : function(v, diff, m, mx){
8334         if(v - diff < m){
8335             diff = v - m;
8336         }else if(v - diff > mx){
8337             diff = mx - v;
8338         }
8339         return diff;
8340     },
8341
8342     // private
8343     onMouseMove : function(e){
8344         
8345         if(this.enabled){
8346             try{// try catch so if something goes wrong the user doesn't get hung
8347
8348             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8349                 return;
8350             }
8351
8352             //var curXY = this.startPoint;
8353             var curSize = this.curSize || this.startBox;
8354             var x = this.startBox.x, y = this.startBox.y;
8355             var ox = x, oy = y;
8356             var w = curSize.width, h = curSize.height;
8357             var ow = w, oh = h;
8358             var mw = this.minWidth, mh = this.minHeight;
8359             var mxw = this.maxWidth, mxh = this.maxHeight;
8360             var wi = this.widthIncrement;
8361             var hi = this.heightIncrement;
8362
8363             var eventXY = e.getXY();
8364             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8365             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8366
8367             var pos = this.activeHandle.position;
8368
8369             switch(pos){
8370                 case "east":
8371                     w += diffX;
8372                     w = Math.min(Math.max(mw, w), mxw);
8373                     break;
8374              
8375                 case "south":
8376                     h += diffY;
8377                     h = Math.min(Math.max(mh, h), mxh);
8378                     break;
8379                 case "southeast":
8380                     w += diffX;
8381                     h += diffY;
8382                     w = Math.min(Math.max(mw, w), mxw);
8383                     h = Math.min(Math.max(mh, h), mxh);
8384                     break;
8385                 case "north":
8386                     diffY = this.constrain(h, diffY, mh, mxh);
8387                     y += diffY;
8388                     h -= diffY;
8389                     break;
8390                 case "hdrag":
8391                     
8392                     if (wi) {
8393                         var adiffX = Math.abs(diffX);
8394                         var sub = (adiffX % wi); // how much 
8395                         if (sub > (wi/2)) { // far enough to snap
8396                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8397                         } else {
8398                             // remove difference.. 
8399                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8400                         }
8401                     }
8402                     x += diffX;
8403                     x = Math.max(this.minX, x);
8404                     break;
8405                 case "west":
8406                     diffX = this.constrain(w, diffX, mw, mxw);
8407                     x += diffX;
8408                     w -= diffX;
8409                     break;
8410                 case "northeast":
8411                     w += diffX;
8412                     w = Math.min(Math.max(mw, w), mxw);
8413                     diffY = this.constrain(h, diffY, mh, mxh);
8414                     y += diffY;
8415                     h -= diffY;
8416                     break;
8417                 case "northwest":
8418                     diffX = this.constrain(w, diffX, mw, mxw);
8419                     diffY = this.constrain(h, diffY, mh, mxh);
8420                     y += diffY;
8421                     h -= diffY;
8422                     x += diffX;
8423                     w -= diffX;
8424                     break;
8425                case "southwest":
8426                     diffX = this.constrain(w, diffX, mw, mxw);
8427                     h += diffY;
8428                     h = Math.min(Math.max(mh, h), mxh);
8429                     x += diffX;
8430                     w -= diffX;
8431                     break;
8432             }
8433
8434             var sw = this.snap(w, wi, mw);
8435             var sh = this.snap(h, hi, mh);
8436             if(sw != w || sh != h){
8437                 switch(pos){
8438                     case "northeast":
8439                         y -= sh - h;
8440                     break;
8441                     case "north":
8442                         y -= sh - h;
8443                         break;
8444                     case "southwest":
8445                         x -= sw - w;
8446                     break;
8447                     case "west":
8448                         x -= sw - w;
8449                         break;
8450                     case "northwest":
8451                         x -= sw - w;
8452                         y -= sh - h;
8453                     break;
8454                 }
8455                 w = sw;
8456                 h = sh;
8457             }
8458
8459             if(this.preserveRatio){
8460                 switch(pos){
8461                     case "southeast":
8462                     case "east":
8463                         h = oh * (w/ow);
8464                         h = Math.min(Math.max(mh, h), mxh);
8465                         w = ow * (h/oh);
8466                        break;
8467                     case "south":
8468                         w = ow * (h/oh);
8469                         w = Math.min(Math.max(mw, w), mxw);
8470                         h = oh * (w/ow);
8471                         break;
8472                     case "northeast":
8473                         w = ow * (h/oh);
8474                         w = Math.min(Math.max(mw, w), mxw);
8475                         h = oh * (w/ow);
8476                     break;
8477                     case "north":
8478                         var tw = w;
8479                         w = ow * (h/oh);
8480                         w = Math.min(Math.max(mw, w), mxw);
8481                         h = oh * (w/ow);
8482                         x += (tw - w) / 2;
8483                         break;
8484                     case "southwest":
8485                         h = oh * (w/ow);
8486                         h = Math.min(Math.max(mh, h), mxh);
8487                         var tw = w;
8488                         w = ow * (h/oh);
8489                         x += tw - w;
8490                         break;
8491                     case "west":
8492                         var th = h;
8493                         h = oh * (w/ow);
8494                         h = Math.min(Math.max(mh, h), mxh);
8495                         y += (th - h) / 2;
8496                         var tw = w;
8497                         w = ow * (h/oh);
8498                         x += tw - w;
8499                        break;
8500                     case "northwest":
8501                         var tw = w;
8502                         var th = h;
8503                         h = oh * (w/ow);
8504                         h = Math.min(Math.max(mh, h), mxh);
8505                         w = ow * (h/oh);
8506                         y += th - h;
8507                         x += tw - w;
8508                        break;
8509
8510                 }
8511             }
8512             if (pos == 'hdrag') {
8513                 w = ow;
8514             }
8515             this.proxy.setBounds(x, y, w, h);
8516             if(this.dynamic){
8517                 this.resizeElement();
8518             }
8519             }catch(e){}
8520         }
8521         this.fireEvent("resizing", this, x, y, w, h, e);
8522     },
8523
8524     // private
8525     handleOver : function(){
8526         if(this.enabled){
8527             this.el.addClass("x-resizable-over");
8528         }
8529     },
8530
8531     // private
8532     handleOut : function(){
8533         if(!this.resizing){
8534             this.el.removeClass("x-resizable-over");
8535         }
8536     },
8537
8538     /**
8539      * Returns the element this component is bound to.
8540      * @return {Roo.Element}
8541      */
8542     getEl : function(){
8543         return this.el;
8544     },
8545
8546     /**
8547      * Returns the resizeChild element (or null).
8548      * @return {Roo.Element}
8549      */
8550     getResizeChild : function(){
8551         return this.resizeChild;
8552     },
8553     groupHandler : function()
8554     {
8555         
8556     },
8557     /**
8558      * Destroys this resizable. If the element was wrapped and
8559      * removeEl is not true then the element remains.
8560      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8561      */
8562     destroy : function(removeEl){
8563         this.proxy.remove();
8564         if(this.overlay){
8565             this.overlay.removeAllListeners();
8566             this.overlay.remove();
8567         }
8568         var ps = Roo.Resizable.positions;
8569         for(var k in ps){
8570             if(typeof ps[k] != "function" && this[ps[k]]){
8571                 var h = this[ps[k]];
8572                 h.el.removeAllListeners();
8573                 h.el.remove();
8574             }
8575         }
8576         if(removeEl){
8577             this.el.update("");
8578             this.el.remove();
8579         }
8580     }
8581 });
8582
8583 // private
8584 // hash to map config positions to true positions
8585 Roo.Resizable.positions = {
8586     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
8587     hd: "hdrag"
8588 };
8589
8590 // private
8591 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8592     if(!this.tpl){
8593         // only initialize the template if resizable is used
8594         var tpl = Roo.DomHelper.createTemplate(
8595             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8596         );
8597         tpl.compile();
8598         Roo.Resizable.Handle.prototype.tpl = tpl;
8599     }
8600     this.position = pos;
8601     this.rz = rz;
8602     // show north drag fro topdra
8603     var handlepos = pos == 'hdrag' ? 'north' : pos;
8604     
8605     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
8606     if (pos == 'hdrag') {
8607         this.el.setStyle('cursor', 'pointer');
8608     }
8609     this.el.unselectable();
8610     if(transparent){
8611         this.el.setOpacity(0);
8612     }
8613     this.el.on("mousedown", this.onMouseDown, this);
8614     if(!disableTrackOver){
8615         this.el.on("mouseover", this.onMouseOver, this);
8616         this.el.on("mouseout", this.onMouseOut, this);
8617     }
8618 };
8619
8620 // private
8621 Roo.Resizable.Handle.prototype = {
8622     afterResize : function(rz){
8623         Roo.log('after?');
8624         // do nothing
8625     },
8626     // private
8627     onMouseDown : function(e){
8628         this.rz.onMouseDown(this, e);
8629     },
8630     // private
8631     onMouseOver : function(e){
8632         this.rz.handleOver(this, e);
8633     },
8634     // private
8635     onMouseOut : function(e){
8636         this.rz.handleOut(this, e);
8637     }
8638 };/*
8639  * Based on:
8640  * Ext JS Library 1.1.1
8641  * Copyright(c) 2006-2007, Ext JS, LLC.
8642  *
8643  * Originally Released Under LGPL - original licence link has changed is not relivant.
8644  *
8645  * Fork - LGPL
8646  * <script type="text/javascript">
8647  */
8648
8649 /**
8650  * @class Roo.Editor
8651  * @extends Roo.Component
8652  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
8653  * @constructor
8654  * Create a new Editor
8655  * @param {Roo.form.Field} field The Field object (or descendant)
8656  * @param {Object} config The config object
8657  */
8658 Roo.Editor = function(field, config){
8659     Roo.Editor.superclass.constructor.call(this, config);
8660     this.field = field;
8661     this.addEvents({
8662         /**
8663              * @event beforestartedit
8664              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
8665              * false from the handler of this event.
8666              * @param {Editor} this
8667              * @param {Roo.Element} boundEl The underlying element bound to this editor
8668              * @param {Mixed} value The field value being set
8669              */
8670         "beforestartedit" : true,
8671         /**
8672              * @event startedit
8673              * Fires when this editor is displayed
8674              * @param {Roo.Element} boundEl The underlying element bound to this editor
8675              * @param {Mixed} value The starting field value
8676              */
8677         "startedit" : true,
8678         /**
8679              * @event beforecomplete
8680              * Fires after a change has been made to the field, but before the change is reflected in the underlying
8681              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
8682              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
8683              * event will not fire since no edit actually occurred.
8684              * @param {Editor} this
8685              * @param {Mixed} value The current field value
8686              * @param {Mixed} startValue The original field value
8687              */
8688         "beforecomplete" : true,
8689         /**
8690              * @event complete
8691              * Fires after editing is complete and any changed value has been written to the underlying field.
8692              * @param {Editor} this
8693              * @param {Mixed} value The current field value
8694              * @param {Mixed} startValue The original field value
8695              */
8696         "complete" : true,
8697         /**
8698          * @event specialkey
8699          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8700          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8701          * @param {Roo.form.Field} this
8702          * @param {Roo.EventObject} e The event object
8703          */
8704         "specialkey" : true
8705     });
8706 };
8707
8708 Roo.extend(Roo.Editor, Roo.Component, {
8709     /**
8710      * @cfg {Boolean/String} autosize
8711      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
8712      * or "height" to adopt the height only (defaults to false)
8713      */
8714     /**
8715      * @cfg {Boolean} revertInvalid
8716      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
8717      * validation fails (defaults to true)
8718      */
8719     /**
8720      * @cfg {Boolean} ignoreNoChange
8721      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
8722      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
8723      * will never be ignored.
8724      */
8725     /**
8726      * @cfg {Boolean} hideEl
8727      * False to keep the bound element visible while the editor is displayed (defaults to true)
8728      */
8729     /**
8730      * @cfg {Mixed} value
8731      * The data value of the underlying field (defaults to "")
8732      */
8733     value : "",
8734     /**
8735      * @cfg {String} alignment
8736      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
8737      */
8738     alignment: "c-c?",
8739     /**
8740      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
8741      * for bottom-right shadow (defaults to "frame")
8742      */
8743     shadow : "frame",
8744     /**
8745      * @cfg {Boolean} constrain True to constrain the editor to the viewport
8746      */
8747     constrain : false,
8748     /**
8749      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
8750      */
8751     completeOnEnter : false,
8752     /**
8753      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
8754      */
8755     cancelOnEsc : false,
8756     /**
8757      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
8758      */
8759     updateEl : false,
8760
8761     // private
8762     onRender : function(ct, position){
8763         this.el = new Roo.Layer({
8764             shadow: this.shadow,
8765             cls: "x-editor",
8766             parentEl : ct,
8767             shim : this.shim,
8768             shadowOffset:4,
8769             id: this.id,
8770             constrain: this.constrain
8771         });
8772         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
8773         if(this.field.msgTarget != 'title'){
8774             this.field.msgTarget = 'qtip';
8775         }
8776         this.field.render(this.el);
8777         if(Roo.isGecko){
8778             this.field.el.dom.setAttribute('autocomplete', 'off');
8779         }
8780         this.field.on("specialkey", this.onSpecialKey, this);
8781         if(this.swallowKeys){
8782             this.field.el.swallowEvent(['keydown','keypress']);
8783         }
8784         this.field.show();
8785         this.field.on("blur", this.onBlur, this);
8786         if(this.field.grow){
8787             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
8788         }
8789     },
8790
8791     onSpecialKey : function(field, e)
8792     {
8793         //Roo.log('editor onSpecialKey');
8794         if(this.completeOnEnter && e.getKey() == e.ENTER){
8795             e.stopEvent();
8796             this.completeEdit();
8797             return;
8798         }
8799         // do not fire special key otherwise it might hide close the editor...
8800         if(e.getKey() == e.ENTER){    
8801             return;
8802         }
8803         if(this.cancelOnEsc && e.getKey() == e.ESC){
8804             this.cancelEdit();
8805             return;
8806         } 
8807         this.fireEvent('specialkey', field, e);
8808     
8809     },
8810
8811     /**
8812      * Starts the editing process and shows the editor.
8813      * @param {String/HTMLElement/Element} el The element to edit
8814      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
8815       * to the innerHTML of el.
8816      */
8817     startEdit : function(el, value){
8818         if(this.editing){
8819             this.completeEdit();
8820         }
8821         this.boundEl = Roo.get(el);
8822         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
8823         if(!this.rendered){
8824             this.render(this.parentEl || document.body);
8825         }
8826         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
8827             return;
8828         }
8829         this.startValue = v;
8830         this.field.setValue(v);
8831         if(this.autoSize){
8832             var sz = this.boundEl.getSize();
8833             switch(this.autoSize){
8834                 case "width":
8835                 this.setSize(sz.width,  "");
8836                 break;
8837                 case "height":
8838                 this.setSize("",  sz.height);
8839                 break;
8840                 default:
8841                 this.setSize(sz.width,  sz.height);
8842             }
8843         }
8844         this.el.alignTo(this.boundEl, this.alignment);
8845         this.editing = true;
8846         if(Roo.QuickTips){
8847             Roo.QuickTips.disable();
8848         }
8849         this.show();
8850     },
8851
8852     /**
8853      * Sets the height and width of this editor.
8854      * @param {Number} width The new width
8855      * @param {Number} height The new height
8856      */
8857     setSize : function(w, h){
8858         this.field.setSize(w, h);
8859         if(this.el){
8860             this.el.sync();
8861         }
8862     },
8863
8864     /**
8865      * Realigns the editor to the bound field based on the current alignment config value.
8866      */
8867     realign : function(){
8868         this.el.alignTo(this.boundEl, this.alignment);
8869     },
8870
8871     /**
8872      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
8873      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
8874      */
8875     completeEdit : function(remainVisible){
8876         if(!this.editing){
8877             return;
8878         }
8879         var v = this.getValue();
8880         if(this.revertInvalid !== false && !this.field.isValid()){
8881             v = this.startValue;
8882             this.cancelEdit(true);
8883         }
8884         if(String(v) === String(this.startValue) && this.ignoreNoChange){
8885             this.editing = false;
8886             this.hide();
8887             return;
8888         }
8889         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
8890             this.editing = false;
8891             if(this.updateEl && this.boundEl){
8892                 this.boundEl.update(v);
8893             }
8894             if(remainVisible !== true){
8895                 this.hide();
8896             }
8897             this.fireEvent("complete", this, v, this.startValue);
8898         }
8899     },
8900
8901     // private
8902     onShow : function(){
8903         this.el.show();
8904         if(this.hideEl !== false){
8905             this.boundEl.hide();
8906         }
8907         this.field.show();
8908         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
8909             this.fixIEFocus = true;
8910             this.deferredFocus.defer(50, this);
8911         }else{
8912             this.field.focus();
8913         }
8914         this.fireEvent("startedit", this.boundEl, this.startValue);
8915     },
8916
8917     deferredFocus : function(){
8918         if(this.editing){
8919             this.field.focus();
8920         }
8921     },
8922
8923     /**
8924      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
8925      * reverted to the original starting value.
8926      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
8927      * cancel (defaults to false)
8928      */
8929     cancelEdit : function(remainVisible){
8930         if(this.editing){
8931             this.setValue(this.startValue);
8932             if(remainVisible !== true){
8933                 this.hide();
8934             }
8935         }
8936     },
8937
8938     // private
8939     onBlur : function(){
8940         if(this.allowBlur !== true && this.editing){
8941             this.completeEdit();
8942         }
8943     },
8944
8945     // private
8946     onHide : function(){
8947         if(this.editing){
8948             this.completeEdit();
8949             return;
8950         }
8951         this.field.blur();
8952         if(this.field.collapse){
8953             this.field.collapse();
8954         }
8955         this.el.hide();
8956         if(this.hideEl !== false){
8957             this.boundEl.show();
8958         }
8959         if(Roo.QuickTips){
8960             Roo.QuickTips.enable();
8961         }
8962     },
8963
8964     /**
8965      * Sets the data value of the editor
8966      * @param {Mixed} value Any valid value supported by the underlying field
8967      */
8968     setValue : function(v){
8969         this.field.setValue(v);
8970     },
8971
8972     /**
8973      * Gets the data value of the editor
8974      * @return {Mixed} The data value
8975      */
8976     getValue : function(){
8977         return this.field.getValue();
8978     }
8979 });/*
8980  * Based on:
8981  * Ext JS Library 1.1.1
8982  * Copyright(c) 2006-2007, Ext JS, LLC.
8983  *
8984  * Originally Released Under LGPL - original licence link has changed is not relivant.
8985  *
8986  * Fork - LGPL
8987  * <script type="text/javascript">
8988  */
8989  
8990 /**
8991  * @class Roo.BasicDialog
8992  * @extends Roo.util.Observable
8993  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
8994  * <pre><code>
8995 var dlg = new Roo.BasicDialog("my-dlg", {
8996     height: 200,
8997     width: 300,
8998     minHeight: 100,
8999     minWidth: 150,
9000     modal: true,
9001     proxyDrag: true,
9002     shadow: true
9003 });
9004 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9005 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
9006 dlg.addButton('Cancel', dlg.hide, dlg);
9007 dlg.show();
9008 </code></pre>
9009   <b>A Dialog should always be a direct child of the body element.</b>
9010  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9011  * @cfg {String} title Default text to display in the title bar (defaults to null)
9012  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9013  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9014  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9015  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9016  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9017  * (defaults to null with no animation)
9018  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9019  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9020  * property for valid values (defaults to 'all')
9021  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9022  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9023  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9024  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9025  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9026  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9027  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9028  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9029  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9030  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9031  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9032  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9033  * draggable = true (defaults to false)
9034  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9035  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9036  * shadow (defaults to false)
9037  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9038  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9039  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9040  * @cfg {Array} buttons Array of buttons
9041  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9042  * @constructor
9043  * Create a new BasicDialog.
9044  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9045  * @param {Object} config Configuration options
9046  */
9047 Roo.BasicDialog = function(el, config){
9048     this.el = Roo.get(el);
9049     var dh = Roo.DomHelper;
9050     if(!this.el && config && config.autoCreate){
9051         if(typeof config.autoCreate == "object"){
9052             if(!config.autoCreate.id){
9053                 config.autoCreate.id = el;
9054             }
9055             this.el = dh.append(document.body,
9056                         config.autoCreate, true);
9057         }else{
9058             this.el = dh.append(document.body,
9059                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9060         }
9061     }
9062     el = this.el;
9063     el.setDisplayed(true);
9064     el.hide = this.hideAction;
9065     this.id = el.id;
9066     el.addClass("x-dlg");
9067
9068     Roo.apply(this, config);
9069
9070     this.proxy = el.createProxy("x-dlg-proxy");
9071     this.proxy.hide = this.hideAction;
9072     this.proxy.setOpacity(.5);
9073     this.proxy.hide();
9074
9075     if(config.width){
9076         el.setWidth(config.width);
9077     }
9078     if(config.height){
9079         el.setHeight(config.height);
9080     }
9081     this.size = el.getSize();
9082     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9083         this.xy = [config.x,config.y];
9084     }else{
9085         this.xy = el.getCenterXY(true);
9086     }
9087     /** The header element @type Roo.Element */
9088     this.header = el.child("> .x-dlg-hd");
9089     /** The body element @type Roo.Element */
9090     this.body = el.child("> .x-dlg-bd");
9091     /** The footer element @type Roo.Element */
9092     this.footer = el.child("> .x-dlg-ft");
9093
9094     if(!this.header){
9095         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9096     }
9097     if(!this.body){
9098         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9099     }
9100
9101     this.header.unselectable();
9102     if(this.title){
9103         this.header.update(this.title);
9104     }
9105     // this element allows the dialog to be focused for keyboard event
9106     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9107     this.focusEl.swallowEvent("click", true);
9108
9109     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9110
9111     // wrap the body and footer for special rendering
9112     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9113     if(this.footer){
9114         this.bwrap.dom.appendChild(this.footer.dom);
9115     }
9116
9117     this.bg = this.el.createChild({
9118         tag: "div", cls:"x-dlg-bg",
9119         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9120     });
9121     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9122
9123
9124     if(this.autoScroll !== false && !this.autoTabs){
9125         this.body.setStyle("overflow", "auto");
9126     }
9127
9128     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9129
9130     if(this.closable !== false){
9131         this.el.addClass("x-dlg-closable");
9132         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9133         this.close.on("click", this.closeClick, this);
9134         this.close.addClassOnOver("x-dlg-close-over");
9135     }
9136     if(this.collapsible !== false){
9137         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9138         this.collapseBtn.on("click", this.collapseClick, this);
9139         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9140         this.header.on("dblclick", this.collapseClick, this);
9141     }
9142     if(this.resizable !== false){
9143         this.el.addClass("x-dlg-resizable");
9144         this.resizer = new Roo.Resizable(el, {
9145             minWidth: this.minWidth || 80,
9146             minHeight:this.minHeight || 80,
9147             handles: this.resizeHandles || "all",
9148             pinned: true
9149         });
9150         this.resizer.on("beforeresize", this.beforeResize, this);
9151         this.resizer.on("resize", this.onResize, this);
9152     }
9153     if(this.draggable !== false){
9154         el.addClass("x-dlg-draggable");
9155         if (!this.proxyDrag) {
9156             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9157         }
9158         else {
9159             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9160         }
9161         dd.setHandleElId(this.header.id);
9162         dd.endDrag = this.endMove.createDelegate(this);
9163         dd.startDrag = this.startMove.createDelegate(this);
9164         dd.onDrag = this.onDrag.createDelegate(this);
9165         dd.scroll = false;
9166         this.dd = dd;
9167     }
9168     if(this.modal){
9169         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9170         this.mask.enableDisplayMode("block");
9171         this.mask.hide();
9172         this.el.addClass("x-dlg-modal");
9173     }
9174     if(this.shadow){
9175         this.shadow = new Roo.Shadow({
9176             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9177             offset : this.shadowOffset
9178         });
9179     }else{
9180         this.shadowOffset = 0;
9181     }
9182     if(Roo.useShims && this.shim !== false){
9183         this.shim = this.el.createShim();
9184         this.shim.hide = this.hideAction;
9185         this.shim.hide();
9186     }else{
9187         this.shim = false;
9188     }
9189     if(this.autoTabs){
9190         this.initTabs();
9191     }
9192     if (this.buttons) { 
9193         var bts= this.buttons;
9194         this.buttons = [];
9195         Roo.each(bts, function(b) {
9196             this.addButton(b);
9197         }, this);
9198     }
9199     
9200     
9201     this.addEvents({
9202         /**
9203          * @event keydown
9204          * Fires when a key is pressed
9205          * @param {Roo.BasicDialog} this
9206          * @param {Roo.EventObject} e
9207          */
9208         "keydown" : true,
9209         /**
9210          * @event move
9211          * Fires when this dialog is moved by the user.
9212          * @param {Roo.BasicDialog} this
9213          * @param {Number} x The new page X
9214          * @param {Number} y The new page Y
9215          */
9216         "move" : true,
9217         /**
9218          * @event resize
9219          * Fires when this dialog is resized by the user.
9220          * @param {Roo.BasicDialog} this
9221          * @param {Number} width The new width
9222          * @param {Number} height The new height
9223          */
9224         "resize" : true,
9225         /**
9226          * @event beforehide
9227          * Fires before this dialog is hidden.
9228          * @param {Roo.BasicDialog} this
9229          */
9230         "beforehide" : true,
9231         /**
9232          * @event hide
9233          * Fires when this dialog is hidden.
9234          * @param {Roo.BasicDialog} this
9235          */
9236         "hide" : true,
9237         /**
9238          * @event beforeshow
9239          * Fires before this dialog is shown.
9240          * @param {Roo.BasicDialog} this
9241          */
9242         "beforeshow" : true,
9243         /**
9244          * @event show
9245          * Fires when this dialog is shown.
9246          * @param {Roo.BasicDialog} this
9247          */
9248         "show" : true
9249     });
9250     el.on("keydown", this.onKeyDown, this);
9251     el.on("mousedown", this.toFront, this);
9252     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9253     this.el.hide();
9254     Roo.DialogManager.register(this);
9255     Roo.BasicDialog.superclass.constructor.call(this);
9256 };
9257
9258 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9259     shadowOffset: Roo.isIE ? 6 : 5,
9260     minHeight: 80,
9261     minWidth: 200,
9262     minButtonWidth: 75,
9263     defaultButton: null,
9264     buttonAlign: "right",
9265     tabTag: 'div',
9266     firstShow: true,
9267
9268     /**
9269      * Sets the dialog title text
9270      * @param {String} text The title text to display
9271      * @return {Roo.BasicDialog} this
9272      */
9273     setTitle : function(text){
9274         this.header.update(text);
9275         return this;
9276     },
9277
9278     // private
9279     closeClick : function(){
9280         this.hide();
9281     },
9282
9283     // private
9284     collapseClick : function(){
9285         this[this.collapsed ? "expand" : "collapse"]();
9286     },
9287
9288     /**
9289      * Collapses the dialog to its minimized state (only the title bar is visible).
9290      * Equivalent to the user clicking the collapse dialog button.
9291      */
9292     collapse : function(){
9293         if(!this.collapsed){
9294             this.collapsed = true;
9295             this.el.addClass("x-dlg-collapsed");
9296             this.restoreHeight = this.el.getHeight();
9297             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9298         }
9299     },
9300
9301     /**
9302      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9303      * clicking the expand dialog button.
9304      */
9305     expand : function(){
9306         if(this.collapsed){
9307             this.collapsed = false;
9308             this.el.removeClass("x-dlg-collapsed");
9309             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9310         }
9311     },
9312
9313     /**
9314      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9315      * @return {Roo.TabPanel} The tabs component
9316      */
9317     initTabs : function(){
9318         var tabs = this.getTabs();
9319         while(tabs.getTab(0)){
9320             tabs.removeTab(0);
9321         }
9322         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9323             var dom = el.dom;
9324             tabs.addTab(Roo.id(dom), dom.title);
9325             dom.title = "";
9326         });
9327         tabs.activate(0);
9328         return tabs;
9329     },
9330
9331     // private
9332     beforeResize : function(){
9333         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9334     },
9335
9336     // private
9337     onResize : function(){
9338         this.refreshSize();
9339         this.syncBodyHeight();
9340         this.adjustAssets();
9341         this.focus();
9342         this.fireEvent("resize", this, this.size.width, this.size.height);
9343     },
9344
9345     // private
9346     onKeyDown : function(e){
9347         if(this.isVisible()){
9348             this.fireEvent("keydown", this, e);
9349         }
9350     },
9351
9352     /**
9353      * Resizes the dialog.
9354      * @param {Number} width
9355      * @param {Number} height
9356      * @return {Roo.BasicDialog} this
9357      */
9358     resizeTo : function(width, height){
9359         this.el.setSize(width, height);
9360         this.size = {width: width, height: height};
9361         this.syncBodyHeight();
9362         if(this.fixedcenter){
9363             this.center();
9364         }
9365         if(this.isVisible()){
9366             this.constrainXY();
9367             this.adjustAssets();
9368         }
9369         this.fireEvent("resize", this, width, height);
9370         return this;
9371     },
9372
9373
9374     /**
9375      * Resizes the dialog to fit the specified content size.
9376      * @param {Number} width
9377      * @param {Number} height
9378      * @return {Roo.BasicDialog} this
9379      */
9380     setContentSize : function(w, h){
9381         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9382         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9383         //if(!this.el.isBorderBox()){
9384             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9385             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9386         //}
9387         if(this.tabs){
9388             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9389             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9390         }
9391         this.resizeTo(w, h);
9392         return this;
9393     },
9394
9395     /**
9396      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9397      * executed in response to a particular key being pressed while the dialog is active.
9398      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9399      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9400      * @param {Function} fn The function to call
9401      * @param {Object} scope (optional) The scope of the function
9402      * @return {Roo.BasicDialog} this
9403      */
9404     addKeyListener : function(key, fn, scope){
9405         var keyCode, shift, ctrl, alt;
9406         if(typeof key == "object" && !(key instanceof Array)){
9407             keyCode = key["key"];
9408             shift = key["shift"];
9409             ctrl = key["ctrl"];
9410             alt = key["alt"];
9411         }else{
9412             keyCode = key;
9413         }
9414         var handler = function(dlg, e){
9415             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9416                 var k = e.getKey();
9417                 if(keyCode instanceof Array){
9418                     for(var i = 0, len = keyCode.length; i < len; i++){
9419                         if(keyCode[i] == k){
9420                           fn.call(scope || window, dlg, k, e);
9421                           return;
9422                         }
9423                     }
9424                 }else{
9425                     if(k == keyCode){
9426                         fn.call(scope || window, dlg, k, e);
9427                     }
9428                 }
9429             }
9430         };
9431         this.on("keydown", handler);
9432         return this;
9433     },
9434
9435     /**
9436      * Returns the TabPanel component (creates it if it doesn't exist).
9437      * Note: If you wish to simply check for the existence of tabs without creating them,
9438      * check for a null 'tabs' property.
9439      * @return {Roo.TabPanel} The tabs component
9440      */
9441     getTabs : function(){
9442         if(!this.tabs){
9443             this.el.addClass("x-dlg-auto-tabs");
9444             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9445             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9446         }
9447         return this.tabs;
9448     },
9449
9450     /**
9451      * Adds a button to the footer section of the dialog.
9452      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9453      * object or a valid Roo.DomHelper element config
9454      * @param {Function} handler The function called when the button is clicked
9455      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9456      * @return {Roo.Button} The new button
9457      */
9458     addButton : function(config, handler, scope){
9459         var dh = Roo.DomHelper;
9460         if(!this.footer){
9461             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9462         }
9463         if(!this.btnContainer){
9464             var tb = this.footer.createChild({
9465
9466                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9467                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9468             }, null, true);
9469             this.btnContainer = tb.firstChild.firstChild.firstChild;
9470         }
9471         var bconfig = {
9472             handler: handler,
9473             scope: scope,
9474             minWidth: this.minButtonWidth,
9475             hideParent:true
9476         };
9477         if(typeof config == "string"){
9478             bconfig.text = config;
9479         }else{
9480             if(config.tag){
9481                 bconfig.dhconfig = config;
9482             }else{
9483                 Roo.apply(bconfig, config);
9484             }
9485         }
9486         var fc = false;
9487         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9488             bconfig.position = Math.max(0, bconfig.position);
9489             fc = this.btnContainer.childNodes[bconfig.position];
9490         }
9491          
9492         var btn = new Roo.Button(
9493             fc ? 
9494                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9495                 : this.btnContainer.appendChild(document.createElement("td")),
9496             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9497             bconfig
9498         );
9499         this.syncBodyHeight();
9500         if(!this.buttons){
9501             /**
9502              * Array of all the buttons that have been added to this dialog via addButton
9503              * @type Array
9504              */
9505             this.buttons = [];
9506         }
9507         this.buttons.push(btn);
9508         return btn;
9509     },
9510
9511     /**
9512      * Sets the default button to be focused when the dialog is displayed.
9513      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9514      * @return {Roo.BasicDialog} this
9515      */
9516     setDefaultButton : function(btn){
9517         this.defaultButton = btn;
9518         return this;
9519     },
9520
9521     // private
9522     getHeaderFooterHeight : function(safe){
9523         var height = 0;
9524         if(this.header){
9525            height += this.header.getHeight();
9526         }
9527         if(this.footer){
9528            var fm = this.footer.getMargins();
9529             height += (this.footer.getHeight()+fm.top+fm.bottom);
9530         }
9531         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9532         height += this.centerBg.getPadding("tb");
9533         return height;
9534     },
9535
9536     // private
9537     syncBodyHeight : function()
9538     {
9539         var bd = this.body, // the text
9540             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9541             bw = this.bwrap;
9542         var height = this.size.height - this.getHeaderFooterHeight(false);
9543         bd.setHeight(height-bd.getMargins("tb"));
9544         var hh = this.header.getHeight();
9545         var h = this.size.height-hh;
9546         cb.setHeight(h);
9547         
9548         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9549         bw.setHeight(h-cb.getPadding("tb"));
9550         
9551         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9552         bd.setWidth(bw.getWidth(true));
9553         if(this.tabs){
9554             this.tabs.syncHeight();
9555             if(Roo.isIE){
9556                 this.tabs.el.repaint();
9557             }
9558         }
9559     },
9560
9561     /**
9562      * Restores the previous state of the dialog if Roo.state is configured.
9563      * @return {Roo.BasicDialog} this
9564      */
9565     restoreState : function(){
9566         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9567         if(box && box.width){
9568             this.xy = [box.x, box.y];
9569             this.resizeTo(box.width, box.height);
9570         }
9571         return this;
9572     },
9573
9574     // private
9575     beforeShow : function(){
9576         this.expand();
9577         if(this.fixedcenter){
9578             this.xy = this.el.getCenterXY(true);
9579         }
9580         if(this.modal){
9581             Roo.get(document.body).addClass("x-body-masked");
9582             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9583             this.mask.show();
9584         }
9585         this.constrainXY();
9586     },
9587
9588     // private
9589     animShow : function(){
9590         var b = Roo.get(this.animateTarget).getBox();
9591         this.proxy.setSize(b.width, b.height);
9592         this.proxy.setLocation(b.x, b.y);
9593         this.proxy.show();
9594         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9595                     true, .35, this.showEl.createDelegate(this));
9596     },
9597
9598     /**
9599      * Shows the dialog.
9600      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
9601      * @return {Roo.BasicDialog} this
9602      */
9603     show : function(animateTarget){
9604         if (this.fireEvent("beforeshow", this) === false){
9605             return;
9606         }
9607         if(this.syncHeightBeforeShow){
9608             this.syncBodyHeight();
9609         }else if(this.firstShow){
9610             this.firstShow = false;
9611             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
9612         }
9613         this.animateTarget = animateTarget || this.animateTarget;
9614         if(!this.el.isVisible()){
9615             this.beforeShow();
9616             if(this.animateTarget && Roo.get(this.animateTarget)){
9617                 this.animShow();
9618             }else{
9619                 this.showEl();
9620             }
9621         }
9622         return this;
9623     },
9624
9625     // private
9626     showEl : function(){
9627         this.proxy.hide();
9628         this.el.setXY(this.xy);
9629         this.el.show();
9630         this.adjustAssets(true);
9631         this.toFront();
9632         this.focus();
9633         // IE peekaboo bug - fix found by Dave Fenwick
9634         if(Roo.isIE){
9635             this.el.repaint();
9636         }
9637         this.fireEvent("show", this);
9638     },
9639
9640     /**
9641      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
9642      * dialog itself will receive focus.
9643      */
9644     focus : function(){
9645         if(this.defaultButton){
9646             this.defaultButton.focus();
9647         }else{
9648             this.focusEl.focus();
9649         }
9650     },
9651
9652     // private
9653     constrainXY : function(){
9654         if(this.constraintoviewport !== false){
9655             if(!this.viewSize){
9656                 if(this.container){
9657                     var s = this.container.getSize();
9658                     this.viewSize = [s.width, s.height];
9659                 }else{
9660                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
9661                 }
9662             }
9663             var s = Roo.get(this.container||document).getScroll();
9664
9665             var x = this.xy[0], y = this.xy[1];
9666             var w = this.size.width, h = this.size.height;
9667             var vw = this.viewSize[0], vh = this.viewSize[1];
9668             // only move it if it needs it
9669             var moved = false;
9670             // first validate right/bottom
9671             if(x + w > vw+s.left){
9672                 x = vw - w;
9673                 moved = true;
9674             }
9675             if(y + h > vh+s.top){
9676                 y = vh - h;
9677                 moved = true;
9678             }
9679             // then make sure top/left isn't negative
9680             if(x < s.left){
9681                 x = s.left;
9682                 moved = true;
9683             }
9684             if(y < s.top){
9685                 y = s.top;
9686                 moved = true;
9687             }
9688             if(moved){
9689                 // cache xy
9690                 this.xy = [x, y];
9691                 if(this.isVisible()){
9692                     this.el.setLocation(x, y);
9693                     this.adjustAssets();
9694                 }
9695             }
9696         }
9697     },
9698
9699     // private
9700     onDrag : function(){
9701         if(!this.proxyDrag){
9702             this.xy = this.el.getXY();
9703             this.adjustAssets();
9704         }
9705     },
9706
9707     // private
9708     adjustAssets : function(doShow){
9709         var x = this.xy[0], y = this.xy[1];
9710         var w = this.size.width, h = this.size.height;
9711         if(doShow === true){
9712             if(this.shadow){
9713                 this.shadow.show(this.el);
9714             }
9715             if(this.shim){
9716                 this.shim.show();
9717             }
9718         }
9719         if(this.shadow && this.shadow.isVisible()){
9720             this.shadow.show(this.el);
9721         }
9722         if(this.shim && this.shim.isVisible()){
9723             this.shim.setBounds(x, y, w, h);
9724         }
9725     },
9726
9727     // private
9728     adjustViewport : function(w, h){
9729         if(!w || !h){
9730             w = Roo.lib.Dom.getViewWidth();
9731             h = Roo.lib.Dom.getViewHeight();
9732         }
9733         // cache the size
9734         this.viewSize = [w, h];
9735         if(this.modal && this.mask.isVisible()){
9736             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
9737             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9738         }
9739         if(this.isVisible()){
9740             this.constrainXY();
9741         }
9742     },
9743
9744     /**
9745      * Destroys this dialog and all its supporting elements (including any tabs, shim,
9746      * shadow, proxy, mask, etc.)  Also removes all event listeners.
9747      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
9748      */
9749     destroy : function(removeEl){
9750         if(this.isVisible()){
9751             this.animateTarget = null;
9752             this.hide();
9753         }
9754         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
9755         if(this.tabs){
9756             this.tabs.destroy(removeEl);
9757         }
9758         Roo.destroy(
9759              this.shim,
9760              this.proxy,
9761              this.resizer,
9762              this.close,
9763              this.mask
9764         );
9765         if(this.dd){
9766             this.dd.unreg();
9767         }
9768         if(this.buttons){
9769            for(var i = 0, len = this.buttons.length; i < len; i++){
9770                this.buttons[i].destroy();
9771            }
9772         }
9773         this.el.removeAllListeners();
9774         if(removeEl === true){
9775             this.el.update("");
9776             this.el.remove();
9777         }
9778         Roo.DialogManager.unregister(this);
9779     },
9780
9781     // private
9782     startMove : function(){
9783         if(this.proxyDrag){
9784             this.proxy.show();
9785         }
9786         if(this.constraintoviewport !== false){
9787             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
9788         }
9789     },
9790
9791     // private
9792     endMove : function(){
9793         if(!this.proxyDrag){
9794             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
9795         }else{
9796             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
9797             this.proxy.hide();
9798         }
9799         this.refreshSize();
9800         this.adjustAssets();
9801         this.focus();
9802         this.fireEvent("move", this, this.xy[0], this.xy[1]);
9803     },
9804
9805     /**
9806      * Brings this dialog to the front of any other visible dialogs
9807      * @return {Roo.BasicDialog} this
9808      */
9809     toFront : function(){
9810         Roo.DialogManager.bringToFront(this);
9811         return this;
9812     },
9813
9814     /**
9815      * Sends this dialog to the back (under) of any other visible dialogs
9816      * @return {Roo.BasicDialog} this
9817      */
9818     toBack : function(){
9819         Roo.DialogManager.sendToBack(this);
9820         return this;
9821     },
9822
9823     /**
9824      * Centers this dialog in the viewport
9825      * @return {Roo.BasicDialog} this
9826      */
9827     center : function(){
9828         var xy = this.el.getCenterXY(true);
9829         this.moveTo(xy[0], xy[1]);
9830         return this;
9831     },
9832
9833     /**
9834      * Moves the dialog's top-left corner to the specified point
9835      * @param {Number} x
9836      * @param {Number} y
9837      * @return {Roo.BasicDialog} this
9838      */
9839     moveTo : function(x, y){
9840         this.xy = [x,y];
9841         if(this.isVisible()){
9842             this.el.setXY(this.xy);
9843             this.adjustAssets();
9844         }
9845         return this;
9846     },
9847
9848     /**
9849      * Aligns the dialog to the specified element
9850      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9851      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
9852      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9853      * @return {Roo.BasicDialog} this
9854      */
9855     alignTo : function(element, position, offsets){
9856         this.xy = this.el.getAlignToXY(element, position, offsets);
9857         if(this.isVisible()){
9858             this.el.setXY(this.xy);
9859             this.adjustAssets();
9860         }
9861         return this;
9862     },
9863
9864     /**
9865      * Anchors an element to another element and realigns it when the window is resized.
9866      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9867      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
9868      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9869      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
9870      * is a number, it is used as the buffer delay (defaults to 50ms).
9871      * @return {Roo.BasicDialog} this
9872      */
9873     anchorTo : function(el, alignment, offsets, monitorScroll){
9874         var action = function(){
9875             this.alignTo(el, alignment, offsets);
9876         };
9877         Roo.EventManager.onWindowResize(action, this);
9878         var tm = typeof monitorScroll;
9879         if(tm != 'undefined'){
9880             Roo.EventManager.on(window, 'scroll', action, this,
9881                 {buffer: tm == 'number' ? monitorScroll : 50});
9882         }
9883         action.call(this);
9884         return this;
9885     },
9886
9887     /**
9888      * Returns true if the dialog is visible
9889      * @return {Boolean}
9890      */
9891     isVisible : function(){
9892         return this.el.isVisible();
9893     },
9894
9895     // private
9896     animHide : function(callback){
9897         var b = Roo.get(this.animateTarget).getBox();
9898         this.proxy.show();
9899         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
9900         this.el.hide();
9901         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
9902                     this.hideEl.createDelegate(this, [callback]));
9903     },
9904
9905     /**
9906      * Hides the dialog.
9907      * @param {Function} callback (optional) Function to call when the dialog is hidden
9908      * @return {Roo.BasicDialog} this
9909      */
9910     hide : function(callback){
9911         if (this.fireEvent("beforehide", this) === false){
9912             return;
9913         }
9914         if(this.shadow){
9915             this.shadow.hide();
9916         }
9917         if(this.shim) {
9918           this.shim.hide();
9919         }
9920         // sometimes animateTarget seems to get set.. causing problems...
9921         // this just double checks..
9922         if(this.animateTarget && Roo.get(this.animateTarget)) {
9923            this.animHide(callback);
9924         }else{
9925             this.el.hide();
9926             this.hideEl(callback);
9927         }
9928         return this;
9929     },
9930
9931     // private
9932     hideEl : function(callback){
9933         this.proxy.hide();
9934         if(this.modal){
9935             this.mask.hide();
9936             Roo.get(document.body).removeClass("x-body-masked");
9937         }
9938         this.fireEvent("hide", this);
9939         if(typeof callback == "function"){
9940             callback();
9941         }
9942     },
9943
9944     // private
9945     hideAction : function(){
9946         this.setLeft("-10000px");
9947         this.setTop("-10000px");
9948         this.setStyle("visibility", "hidden");
9949     },
9950
9951     // private
9952     refreshSize : function(){
9953         this.size = this.el.getSize();
9954         this.xy = this.el.getXY();
9955         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
9956     },
9957
9958     // private
9959     // z-index is managed by the DialogManager and may be overwritten at any time
9960     setZIndex : function(index){
9961         if(this.modal){
9962             this.mask.setStyle("z-index", index);
9963         }
9964         if(this.shim){
9965             this.shim.setStyle("z-index", ++index);
9966         }
9967         if(this.shadow){
9968             this.shadow.setZIndex(++index);
9969         }
9970         this.el.setStyle("z-index", ++index);
9971         if(this.proxy){
9972             this.proxy.setStyle("z-index", ++index);
9973         }
9974         if(this.resizer){
9975             this.resizer.proxy.setStyle("z-index", ++index);
9976         }
9977
9978         this.lastZIndex = index;
9979     },
9980
9981     /**
9982      * Returns the element for this dialog
9983      * @return {Roo.Element} The underlying dialog Element
9984      */
9985     getEl : function(){
9986         return this.el;
9987     }
9988 });
9989
9990 /**
9991  * @class Roo.DialogManager
9992  * Provides global access to BasicDialogs that have been created and
9993  * support for z-indexing (layering) multiple open dialogs.
9994  */
9995 Roo.DialogManager = function(){
9996     var list = {};
9997     var accessList = [];
9998     var front = null;
9999
10000     // private
10001     var sortDialogs = function(d1, d2){
10002         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10003     };
10004
10005     // private
10006     var orderDialogs = function(){
10007         accessList.sort(sortDialogs);
10008         var seed = Roo.DialogManager.zseed;
10009         for(var i = 0, len = accessList.length; i < len; i++){
10010             var dlg = accessList[i];
10011             if(dlg){
10012                 dlg.setZIndex(seed + (i*10));
10013             }
10014         }
10015     };
10016
10017     return {
10018         /**
10019          * The starting z-index for BasicDialogs (defaults to 9000)
10020          * @type Number The z-index value
10021          */
10022         zseed : 9000,
10023
10024         // private
10025         register : function(dlg){
10026             list[dlg.id] = dlg;
10027             accessList.push(dlg);
10028         },
10029
10030         // private
10031         unregister : function(dlg){
10032             delete list[dlg.id];
10033             var i=0;
10034             var len=0;
10035             if(!accessList.indexOf){
10036                 for(  i = 0, len = accessList.length; i < len; i++){
10037                     if(accessList[i] == dlg){
10038                         accessList.splice(i, 1);
10039                         return;
10040                     }
10041                 }
10042             }else{
10043                  i = accessList.indexOf(dlg);
10044                 if(i != -1){
10045                     accessList.splice(i, 1);
10046                 }
10047             }
10048         },
10049
10050         /**
10051          * Gets a registered dialog by id
10052          * @param {String/Object} id The id of the dialog or a dialog
10053          * @return {Roo.BasicDialog} this
10054          */
10055         get : function(id){
10056             return typeof id == "object" ? id : list[id];
10057         },
10058
10059         /**
10060          * Brings the specified dialog to the front
10061          * @param {String/Object} dlg The id of the dialog or a dialog
10062          * @return {Roo.BasicDialog} this
10063          */
10064         bringToFront : function(dlg){
10065             dlg = this.get(dlg);
10066             if(dlg != front){
10067                 front = dlg;
10068                 dlg._lastAccess = new Date().getTime();
10069                 orderDialogs();
10070             }
10071             return dlg;
10072         },
10073
10074         /**
10075          * Sends the specified dialog to the back
10076          * @param {String/Object} dlg The id of the dialog or a dialog
10077          * @return {Roo.BasicDialog} this
10078          */
10079         sendToBack : function(dlg){
10080             dlg = this.get(dlg);
10081             dlg._lastAccess = -(new Date().getTime());
10082             orderDialogs();
10083             return dlg;
10084         },
10085
10086         /**
10087          * Hides all dialogs
10088          */
10089         hideAll : function(){
10090             for(var id in list){
10091                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10092                     list[id].hide();
10093                 }
10094             }
10095         }
10096     };
10097 }();
10098
10099 /**
10100  * @class Roo.LayoutDialog
10101  * @extends Roo.BasicDialog
10102  * @children Roo.ContentPanel
10103  * @parent builder none
10104  * Dialog which provides adjustments for working with a layout in a Dialog.
10105  * Add your necessary layout config options to the dialog's config.<br>
10106  * Example usage (including a nested layout):
10107  * <pre><code>
10108 if(!dialog){
10109     dialog = new Roo.LayoutDialog("download-dlg", {
10110         modal: true,
10111         width:600,
10112         height:450,
10113         shadow:true,
10114         minWidth:500,
10115         minHeight:350,
10116         autoTabs:true,
10117         proxyDrag:true,
10118         // layout config merges with the dialog config
10119         center:{
10120             tabPosition: "top",
10121             alwaysShowTabs: true
10122         }
10123     });
10124     dialog.addKeyListener(27, dialog.hide, dialog);
10125     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10126     dialog.addButton("Build It!", this.getDownload, this);
10127
10128     // we can even add nested layouts
10129     var innerLayout = new Roo.BorderLayout("dl-inner", {
10130         east: {
10131             initialSize: 200,
10132             autoScroll:true,
10133             split:true
10134         },
10135         center: {
10136             autoScroll:true
10137         }
10138     });
10139     innerLayout.beginUpdate();
10140     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10141     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10142     innerLayout.endUpdate(true);
10143
10144     var layout = dialog.getLayout();
10145     layout.beginUpdate();
10146     layout.add("center", new Roo.ContentPanel("standard-panel",
10147                         {title: "Download the Source", fitToFrame:true}));
10148     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10149                {title: "Build your own roo.js"}));
10150     layout.getRegion("center").showPanel(sp);
10151     layout.endUpdate();
10152 }
10153 </code></pre>
10154     * @constructor
10155     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10156     * @param {Object} config configuration options
10157   */
10158 Roo.LayoutDialog = function(el, cfg){
10159     
10160     var config=  cfg;
10161     if (typeof(cfg) == 'undefined') {
10162         config = Roo.apply({}, el);
10163         // not sure why we use documentElement here.. - it should always be body.
10164         // IE7 borks horribly if we use documentElement.
10165         // webkit also does not like documentElement - it creates a body element...
10166         el = Roo.get( document.body || document.documentElement ).createChild();
10167         //config.autoCreate = true;
10168     }
10169     
10170     
10171     config.autoTabs = false;
10172     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10173     this.body.setStyle({overflow:"hidden", position:"relative"});
10174     this.layout = new Roo.BorderLayout(this.body.dom, config);
10175     this.layout.monitorWindowResize = false;
10176     this.el.addClass("x-dlg-auto-layout");
10177     // fix case when center region overwrites center function
10178     this.center = Roo.BasicDialog.prototype.center;
10179     this.on("show", this.layout.layout, this.layout, true);
10180     if (config.items) {
10181         var xitems = config.items;
10182         delete config.items;
10183         Roo.each(xitems, this.addxtype, this);
10184     }
10185     
10186     
10187 };
10188 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10189     
10190     
10191     /**
10192      * @cfg {Roo.LayoutRegion} east  
10193      */
10194     /**
10195      * @cfg {Roo.LayoutRegion} west
10196      */
10197     /**
10198      * @cfg {Roo.LayoutRegion} south
10199      */
10200     /**
10201      * @cfg {Roo.LayoutRegion} north
10202      */
10203     /**
10204      * @cfg {Roo.LayoutRegion} center
10205      */
10206     /**
10207      * @cfg {Roo.Button} buttons[]  Bottom buttons..
10208      */
10209     
10210     
10211     /**
10212      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10213      * @deprecated
10214      */
10215     endUpdate : function(){
10216         this.layout.endUpdate();
10217     },
10218
10219     /**
10220      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10221      *  @deprecated
10222      */
10223     beginUpdate : function(){
10224         this.layout.beginUpdate();
10225     },
10226
10227     /**
10228      * Get the BorderLayout for this dialog
10229      * @return {Roo.BorderLayout}
10230      */
10231     getLayout : function(){
10232         return this.layout;
10233     },
10234
10235     showEl : function(){
10236         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10237         if(Roo.isIE7){
10238             this.layout.layout();
10239         }
10240     },
10241
10242     // private
10243     // Use the syncHeightBeforeShow config option to control this automatically
10244     syncBodyHeight : function(){
10245         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10246         if(this.layout){this.layout.layout();}
10247     },
10248     
10249       /**
10250      * Add an xtype element (actually adds to the layout.)
10251      * @return {Object} xdata xtype object data.
10252      */
10253     
10254     addxtype : function(c) {
10255         return this.layout.addxtype(c);
10256     }
10257 });/*
10258  * Based on:
10259  * Ext JS Library 1.1.1
10260  * Copyright(c) 2006-2007, Ext JS, LLC.
10261  *
10262  * Originally Released Under LGPL - original licence link has changed is not relivant.
10263  *
10264  * Fork - LGPL
10265  * <script type="text/javascript">
10266  */
10267  
10268 /**
10269  * @class Roo.MessageBox
10270  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10271  * Example usage:
10272  *<pre><code>
10273 // Basic alert:
10274 Roo.Msg.alert('Status', 'Changes saved successfully.');
10275
10276 // Prompt for user data:
10277 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10278     if (btn == 'ok'){
10279         // process text value...
10280     }
10281 });
10282
10283 // Show a dialog using config options:
10284 Roo.Msg.show({
10285    title:'Save Changes?',
10286    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10287    buttons: Roo.Msg.YESNOCANCEL,
10288    fn: processResult,
10289    animEl: 'elId'
10290 });
10291 </code></pre>
10292  * @static
10293  */
10294 Roo.MessageBox = function(){
10295     var dlg, opt, mask, waitTimer;
10296     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10297     var buttons, activeTextEl, bwidth;
10298
10299     // private
10300     var handleButton = function(button){
10301         dlg.hide();
10302         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10303     };
10304
10305     // private
10306     var handleHide = function(){
10307         if(opt && opt.cls){
10308             dlg.el.removeClass(opt.cls);
10309         }
10310         if(waitTimer){
10311             Roo.TaskMgr.stop(waitTimer);
10312             waitTimer = null;
10313         }
10314     };
10315
10316     // private
10317     var updateButtons = function(b){
10318         var width = 0;
10319         if(!b){
10320             buttons["ok"].hide();
10321             buttons["cancel"].hide();
10322             buttons["yes"].hide();
10323             buttons["no"].hide();
10324             dlg.footer.dom.style.display = 'none';
10325             return width;
10326         }
10327         dlg.footer.dom.style.display = '';
10328         for(var k in buttons){
10329             if(typeof buttons[k] != "function"){
10330                 if(b[k]){
10331                     buttons[k].show();
10332                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10333                     width += buttons[k].el.getWidth()+15;
10334                 }else{
10335                     buttons[k].hide();
10336                 }
10337             }
10338         }
10339         return width;
10340     };
10341
10342     // private
10343     var handleEsc = function(d, k, e){
10344         if(opt && opt.closable !== false){
10345             dlg.hide();
10346         }
10347         if(e){
10348             e.stopEvent();
10349         }
10350     };
10351
10352     return {
10353         /**
10354          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10355          * @return {Roo.BasicDialog} The BasicDialog element
10356          */
10357         getDialog : function(){
10358            if(!dlg){
10359                 dlg = new Roo.BasicDialog("x-msg-box", {
10360                     autoCreate : true,
10361                     shadow: true,
10362                     draggable: true,
10363                     resizable:false,
10364                     constraintoviewport:false,
10365                     fixedcenter:true,
10366                     collapsible : false,
10367                     shim:true,
10368                     modal: true,
10369                     width:400, height:100,
10370                     buttonAlign:"center",
10371                     closeClick : function(){
10372                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10373                             handleButton("no");
10374                         }else{
10375                             handleButton("cancel");
10376                         }
10377                     }
10378                 });
10379                 dlg.on("hide", handleHide);
10380                 mask = dlg.mask;
10381                 dlg.addKeyListener(27, handleEsc);
10382                 buttons = {};
10383                 var bt = this.buttonText;
10384                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10385                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10386                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10387                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10388                 bodyEl = dlg.body.createChild({
10389
10390                     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>'
10391                 });
10392                 msgEl = bodyEl.dom.firstChild;
10393                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10394                 textboxEl.enableDisplayMode();
10395                 textboxEl.addKeyListener([10,13], function(){
10396                     if(dlg.isVisible() && opt && opt.buttons){
10397                         if(opt.buttons.ok){
10398                             handleButton("ok");
10399                         }else if(opt.buttons.yes){
10400                             handleButton("yes");
10401                         }
10402                     }
10403                 });
10404                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10405                 textareaEl.enableDisplayMode();
10406                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10407                 progressEl.enableDisplayMode();
10408                 var pf = progressEl.dom.firstChild;
10409                 if (pf) {
10410                     pp = Roo.get(pf.firstChild);
10411                     pp.setHeight(pf.offsetHeight);
10412                 }
10413                 
10414             }
10415             return dlg;
10416         },
10417
10418         /**
10419          * Updates the message box body text
10420          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10421          * the XHTML-compliant non-breaking space character '&amp;#160;')
10422          * @return {Roo.MessageBox} This message box
10423          */
10424         updateText : function(text){
10425             if(!dlg.isVisible() && !opt.width){
10426                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10427             }
10428             msgEl.innerHTML = text || '&#160;';
10429       
10430             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10431             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10432             var w = Math.max(
10433                     Math.min(opt.width || cw , this.maxWidth), 
10434                     Math.max(opt.minWidth || this.minWidth, bwidth)
10435             );
10436             if(opt.prompt){
10437                 activeTextEl.setWidth(w);
10438             }
10439             if(dlg.isVisible()){
10440                 dlg.fixedcenter = false;
10441             }
10442             // to big, make it scroll. = But as usual stupid IE does not support
10443             // !important..
10444             
10445             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10446                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10447                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10448             } else {
10449                 bodyEl.dom.style.height = '';
10450                 bodyEl.dom.style.overflowY = '';
10451             }
10452             if (cw > w) {
10453                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10454             } else {
10455                 bodyEl.dom.style.overflowX = '';
10456             }
10457             
10458             dlg.setContentSize(w, bodyEl.getHeight());
10459             if(dlg.isVisible()){
10460                 dlg.fixedcenter = true;
10461             }
10462             return this;
10463         },
10464
10465         /**
10466          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10467          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10468          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10469          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10470          * @return {Roo.MessageBox} This message box
10471          */
10472         updateProgress : function(value, text){
10473             if(text){
10474                 this.updateText(text);
10475             }
10476             if (pp) { // weird bug on my firefox - for some reason this is not defined
10477                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10478             }
10479             return this;
10480         },        
10481
10482         /**
10483          * Returns true if the message box is currently displayed
10484          * @return {Boolean} True if the message box is visible, else false
10485          */
10486         isVisible : function(){
10487             return dlg && dlg.isVisible();  
10488         },
10489
10490         /**
10491          * Hides the message box if it is displayed
10492          */
10493         hide : function(){
10494             if(this.isVisible()){
10495                 dlg.hide();
10496             }  
10497         },
10498
10499         /**
10500          * Displays a new message box, or reinitializes an existing message box, based on the config options
10501          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10502          * The following config object properties are supported:
10503          * <pre>
10504 Property    Type             Description
10505 ----------  ---------------  ------------------------------------------------------------------------------------
10506 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10507                                    closes (defaults to undefined)
10508 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10509                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10510 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10511                                    progress and wait dialogs will ignore this property and always hide the
10512                                    close button as they can only be closed programmatically.
10513 cls               String           A custom CSS class to apply to the message box element
10514 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10515                                    displayed (defaults to 75)
10516 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10517                                    function will be btn (the name of the button that was clicked, if applicable,
10518                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10519                                    Progress and wait dialogs will ignore this option since they do not respond to
10520                                    user actions and can only be closed programmatically, so any required function
10521                                    should be called by the same code after it closes the dialog.
10522 icon              String           A CSS class that provides a background image to be used as an icon for
10523                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10524 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10525 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10526 modal             Boolean          False to allow user interaction with the page while the message box is
10527                                    displayed (defaults to true)
10528 msg               String           A string that will replace the existing message box body text (defaults
10529                                    to the XHTML-compliant non-breaking space character '&#160;')
10530 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10531 progress          Boolean          True to display a progress bar (defaults to false)
10532 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10533 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10534 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10535 title             String           The title text
10536 value             String           The string value to set into the active textbox element if displayed
10537 wait              Boolean          True to display a progress bar (defaults to false)
10538 width             Number           The width of the dialog in pixels
10539 </pre>
10540          *
10541          * Example usage:
10542          * <pre><code>
10543 Roo.Msg.show({
10544    title: 'Address',
10545    msg: 'Please enter your address:',
10546    width: 300,
10547    buttons: Roo.MessageBox.OKCANCEL,
10548    multiline: true,
10549    fn: saveAddress,
10550    animEl: 'addAddressBtn'
10551 });
10552 </code></pre>
10553          * @param {Object} config Configuration options
10554          * @return {Roo.MessageBox} This message box
10555          */
10556         show : function(options)
10557         {
10558             
10559             // this causes nightmares if you show one dialog after another
10560             // especially on callbacks..
10561              
10562             if(this.isVisible()){
10563                 
10564                 this.hide();
10565                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10566                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10567                 Roo.log("New Dialog Message:" +  options.msg )
10568                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10569                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10570                 
10571             }
10572             var d = this.getDialog();
10573             opt = options;
10574             d.setTitle(opt.title || "&#160;");
10575             d.close.setDisplayed(opt.closable !== false);
10576             activeTextEl = textboxEl;
10577             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10578             if(opt.prompt){
10579                 if(opt.multiline){
10580                     textboxEl.hide();
10581                     textareaEl.show();
10582                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10583                         opt.multiline : this.defaultTextHeight);
10584                     activeTextEl = textareaEl;
10585                 }else{
10586                     textboxEl.show();
10587                     textareaEl.hide();
10588                 }
10589             }else{
10590                 textboxEl.hide();
10591                 textareaEl.hide();
10592             }
10593             progressEl.setDisplayed(opt.progress === true);
10594             this.updateProgress(0);
10595             activeTextEl.dom.value = opt.value || "";
10596             if(opt.prompt){
10597                 dlg.setDefaultButton(activeTextEl);
10598             }else{
10599                 var bs = opt.buttons;
10600                 var db = null;
10601                 if(bs && bs.ok){
10602                     db = buttons["ok"];
10603                 }else if(bs && bs.yes){
10604                     db = buttons["yes"];
10605                 }
10606                 dlg.setDefaultButton(db);
10607             }
10608             bwidth = updateButtons(opt.buttons);
10609             this.updateText(opt.msg);
10610             if(opt.cls){
10611                 d.el.addClass(opt.cls);
10612             }
10613             d.proxyDrag = opt.proxyDrag === true;
10614             d.modal = opt.modal !== false;
10615             d.mask = opt.modal !== false ? mask : false;
10616             if(!d.isVisible()){
10617                 // force it to the end of the z-index stack so it gets a cursor in FF
10618                 document.body.appendChild(dlg.el.dom);
10619                 d.animateTarget = null;
10620                 d.show(options.animEl);
10621             }
10622             return this;
10623         },
10624
10625         /**
10626          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
10627          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
10628          * and closing the message box when the process is complete.
10629          * @param {String} title The title bar text
10630          * @param {String} msg The message box body text
10631          * @return {Roo.MessageBox} This message box
10632          */
10633         progress : function(title, msg){
10634             this.show({
10635                 title : title,
10636                 msg : msg,
10637                 buttons: false,
10638                 progress:true,
10639                 closable:false,
10640                 minWidth: this.minProgressWidth,
10641                 modal : true
10642             });
10643             return this;
10644         },
10645
10646         /**
10647          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
10648          * If a callback function is passed it will be called after the user clicks the button, and the
10649          * id of the button that was clicked will be passed as the only parameter to the callback
10650          * (could also be the top-right close button).
10651          * @param {String} title The title bar text
10652          * @param {String} msg The message box body text
10653          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10654          * @param {Object} scope (optional) The scope of the callback function
10655          * @return {Roo.MessageBox} This message box
10656          */
10657         alert : function(title, msg, fn, scope){
10658             this.show({
10659                 title : title,
10660                 msg : msg,
10661                 buttons: this.OK,
10662                 fn: fn,
10663                 scope : scope,
10664                 modal : true
10665             });
10666             return this;
10667         },
10668
10669         /**
10670          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
10671          * interaction while waiting for a long-running process to complete that does not have defined intervals.
10672          * You are responsible for closing the message box when the process is complete.
10673          * @param {String} msg The message box body text
10674          * @param {String} title (optional) The title bar text
10675          * @return {Roo.MessageBox} This message box
10676          */
10677         wait : function(msg, title){
10678             this.show({
10679                 title : title,
10680                 msg : msg,
10681                 buttons: false,
10682                 closable:false,
10683                 progress:true,
10684                 modal:true,
10685                 width:300,
10686                 wait:true
10687             });
10688             waitTimer = Roo.TaskMgr.start({
10689                 run: function(i){
10690                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
10691                 },
10692                 interval: 1000
10693             });
10694             return this;
10695         },
10696
10697         /**
10698          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
10699          * If a callback function is passed it will be called after the user clicks either button, and the id of the
10700          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
10701          * @param {String} title The title bar text
10702          * @param {String} msg The message box body text
10703          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10704          * @param {Object} scope (optional) The scope of the callback function
10705          * @return {Roo.MessageBox} This message box
10706          */
10707         confirm : function(title, msg, fn, scope){
10708             this.show({
10709                 title : title,
10710                 msg : msg,
10711                 buttons: this.YESNO,
10712                 fn: fn,
10713                 scope : scope,
10714                 modal : true
10715             });
10716             return this;
10717         },
10718
10719         /**
10720          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
10721          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
10722          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
10723          * (could also be the top-right close button) and the text that was entered will be passed as the two
10724          * parameters to the callback.
10725          * @param {String} title The title bar text
10726          * @param {String} msg The message box body text
10727          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10728          * @param {Object} scope (optional) The scope of the callback function
10729          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
10730          * property, or the height in pixels to create the textbox (defaults to false / single-line)
10731          * @return {Roo.MessageBox} This message box
10732          */
10733         prompt : function(title, msg, fn, scope, multiline){
10734             this.show({
10735                 title : title,
10736                 msg : msg,
10737                 buttons: this.OKCANCEL,
10738                 fn: fn,
10739                 minWidth:250,
10740                 scope : scope,
10741                 prompt:true,
10742                 multiline: multiline,
10743                 modal : true
10744             });
10745             return this;
10746         },
10747
10748         /**
10749          * Button config that displays a single OK button
10750          * @type Object
10751          */
10752         OK : {ok:true},
10753         /**
10754          * Button config that displays Yes and No buttons
10755          * @type Object
10756          */
10757         YESNO : {yes:true, no:true},
10758         /**
10759          * Button config that displays OK and Cancel buttons
10760          * @type Object
10761          */
10762         OKCANCEL : {ok:true, cancel:true},
10763         /**
10764          * Button config that displays Yes, No and Cancel buttons
10765          * @type Object
10766          */
10767         YESNOCANCEL : {yes:true, no:true, cancel:true},
10768
10769         /**
10770          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
10771          * @type Number
10772          */
10773         defaultTextHeight : 75,
10774         /**
10775          * The maximum width in pixels of the message box (defaults to 600)
10776          * @type Number
10777          */
10778         maxWidth : 600,
10779         /**
10780          * The minimum width in pixels of the message box (defaults to 100)
10781          * @type Number
10782          */
10783         minWidth : 100,
10784         /**
10785          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
10786          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
10787          * @type Number
10788          */
10789         minProgressWidth : 250,
10790         /**
10791          * An object containing the default button text strings that can be overriden for localized language support.
10792          * Supported properties are: ok, cancel, yes and no.
10793          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
10794          * @type Object
10795          */
10796         buttonText : {
10797             ok : "OK",
10798             cancel : "Cancel",
10799             yes : "Yes",
10800             no : "No"
10801         }
10802     };
10803 }();
10804
10805 /**
10806  * Shorthand for {@link Roo.MessageBox}
10807  */
10808 Roo.Msg = Roo.MessageBox;/*
10809  * Based on:
10810  * Ext JS Library 1.1.1
10811  * Copyright(c) 2006-2007, Ext JS, LLC.
10812  *
10813  * Originally Released Under LGPL - original licence link has changed is not relivant.
10814  *
10815  * Fork - LGPL
10816  * <script type="text/javascript">
10817  */
10818 /**
10819  * @class Roo.QuickTips
10820  * Provides attractive and customizable tooltips for any element.
10821  * @static
10822  */
10823 Roo.QuickTips = function(){
10824     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
10825     var ce, bd, xy, dd;
10826     var visible = false, disabled = true, inited = false;
10827     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
10828     
10829     var onOver = function(e){
10830         if(disabled){
10831             return;
10832         }
10833         var t = e.getTarget();
10834         if(!t || t.nodeType !== 1 || t == document || t == document.body){
10835             return;
10836         }
10837         if(ce && t == ce.el){
10838             clearTimeout(hideProc);
10839             return;
10840         }
10841         if(t && tagEls[t.id]){
10842             tagEls[t.id].el = t;
10843             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
10844             return;
10845         }
10846         var ttp, et = Roo.fly(t);
10847         var ns = cfg.namespace;
10848         if(tm.interceptTitles && t.title){
10849             ttp = t.title;
10850             t.qtip = ttp;
10851             t.removeAttribute("title");
10852             e.preventDefault();
10853         }else{
10854             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
10855         }
10856         if(ttp){
10857             showProc = show.defer(tm.showDelay, tm, [{
10858                 el: t, 
10859                 text: ttp.replace(/\\n/g,'<br/>'),
10860                 width: et.getAttributeNS(ns, cfg.width),
10861                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
10862                 title: et.getAttributeNS(ns, cfg.title),
10863                     cls: et.getAttributeNS(ns, cfg.cls)
10864             }]);
10865         }
10866     };
10867     
10868     var onOut = function(e){
10869         clearTimeout(showProc);
10870         var t = e.getTarget();
10871         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
10872             hideProc = setTimeout(hide, tm.hideDelay);
10873         }
10874     };
10875     
10876     var onMove = function(e){
10877         if(disabled){
10878             return;
10879         }
10880         xy = e.getXY();
10881         xy[1] += 18;
10882         if(tm.trackMouse && ce){
10883             el.setXY(xy);
10884         }
10885     };
10886     
10887     var onDown = function(e){
10888         clearTimeout(showProc);
10889         clearTimeout(hideProc);
10890         if(!e.within(el)){
10891             if(tm.hideOnClick){
10892                 hide();
10893                 tm.disable();
10894                 tm.enable.defer(100, tm);
10895             }
10896         }
10897     };
10898     
10899     var getPad = function(){
10900         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
10901     };
10902
10903     var show = function(o){
10904         if(disabled){
10905             return;
10906         }
10907         clearTimeout(dismissProc);
10908         ce = o;
10909         if(removeCls){ // in case manually hidden
10910             el.removeClass(removeCls);
10911             removeCls = null;
10912         }
10913         if(ce.cls){
10914             el.addClass(ce.cls);
10915             removeCls = ce.cls;
10916         }
10917         if(ce.title){
10918             tipTitle.update(ce.title);
10919             tipTitle.show();
10920         }else{
10921             tipTitle.update('');
10922             tipTitle.hide();
10923         }
10924         el.dom.style.width  = tm.maxWidth+'px';
10925         //tipBody.dom.style.width = '';
10926         tipBodyText.update(o.text);
10927         var p = getPad(), w = ce.width;
10928         if(!w){
10929             var td = tipBodyText.dom;
10930             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
10931             if(aw > tm.maxWidth){
10932                 w = tm.maxWidth;
10933             }else if(aw < tm.minWidth){
10934                 w = tm.minWidth;
10935             }else{
10936                 w = aw;
10937             }
10938         }
10939         //tipBody.setWidth(w);
10940         el.setWidth(parseInt(w, 10) + p);
10941         if(ce.autoHide === false){
10942             close.setDisplayed(true);
10943             if(dd){
10944                 dd.unlock();
10945             }
10946         }else{
10947             close.setDisplayed(false);
10948             if(dd){
10949                 dd.lock();
10950             }
10951         }
10952         if(xy){
10953             el.avoidY = xy[1]-18;
10954             el.setXY(xy);
10955         }
10956         if(tm.animate){
10957             el.setOpacity(.1);
10958             el.setStyle("visibility", "visible");
10959             el.fadeIn({callback: afterShow});
10960         }else{
10961             afterShow();
10962         }
10963     };
10964     
10965     var afterShow = function(){
10966         if(ce){
10967             el.show();
10968             esc.enable();
10969             if(tm.autoDismiss && ce.autoHide !== false){
10970                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
10971             }
10972         }
10973     };
10974     
10975     var hide = function(noanim){
10976         clearTimeout(dismissProc);
10977         clearTimeout(hideProc);
10978         ce = null;
10979         if(el.isVisible()){
10980             esc.disable();
10981             if(noanim !== true && tm.animate){
10982                 el.fadeOut({callback: afterHide});
10983             }else{
10984                 afterHide();
10985             } 
10986         }
10987     };
10988     
10989     var afterHide = function(){
10990         el.hide();
10991         if(removeCls){
10992             el.removeClass(removeCls);
10993             removeCls = null;
10994         }
10995     };
10996     
10997     return {
10998         /**
10999         * @cfg {Number} minWidth
11000         * The minimum width of the quick tip (defaults to 40)
11001         */
11002        minWidth : 40,
11003         /**
11004         * @cfg {Number} maxWidth
11005         * The maximum width of the quick tip (defaults to 300)
11006         */
11007        maxWidth : 300,
11008         /**
11009         * @cfg {Boolean} interceptTitles
11010         * True to automatically use the element's DOM title value if available (defaults to false)
11011         */
11012        interceptTitles : false,
11013         /**
11014         * @cfg {Boolean} trackMouse
11015         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11016         */
11017        trackMouse : false,
11018         /**
11019         * @cfg {Boolean} hideOnClick
11020         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11021         */
11022        hideOnClick : true,
11023         /**
11024         * @cfg {Number} showDelay
11025         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11026         */
11027        showDelay : 500,
11028         /**
11029         * @cfg {Number} hideDelay
11030         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11031         */
11032        hideDelay : 200,
11033         /**
11034         * @cfg {Boolean} autoHide
11035         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11036         * Used in conjunction with hideDelay.
11037         */
11038        autoHide : true,
11039         /**
11040         * @cfg {Boolean}
11041         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11042         * (defaults to true).  Used in conjunction with autoDismissDelay.
11043         */
11044        autoDismiss : true,
11045         /**
11046         * @cfg {Number}
11047         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11048         */
11049        autoDismissDelay : 5000,
11050        /**
11051         * @cfg {Boolean} animate
11052         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11053         */
11054        animate : false,
11055
11056        /**
11057         * @cfg {String} title
11058         * Title text to display (defaults to '').  This can be any valid HTML markup.
11059         */
11060         title: '',
11061        /**
11062         * @cfg {String} text
11063         * Body text to display (defaults to '').  This can be any valid HTML markup.
11064         */
11065         text : '',
11066        /**
11067         * @cfg {String} cls
11068         * A CSS class to apply to the base quick tip element (defaults to '').
11069         */
11070         cls : '',
11071        /**
11072         * @cfg {Number} width
11073         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11074         * minWidth or maxWidth.
11075         */
11076         width : null,
11077
11078     /**
11079      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11080      * or display QuickTips in a page.
11081      */
11082        init : function(){
11083           tm = Roo.QuickTips;
11084           cfg = tm.tagConfig;
11085           if(!inited){
11086               if(!Roo.isReady){ // allow calling of init() before onReady
11087                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11088                   return;
11089               }
11090               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11091               el.fxDefaults = {stopFx: true};
11092               // maximum custom styling
11093               //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>');
11094               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>');              
11095               tipTitle = el.child('h3');
11096               tipTitle.enableDisplayMode("block");
11097               tipBody = el.child('div.x-tip-bd');
11098               tipBodyText = el.child('div.x-tip-bd-inner');
11099               //bdLeft = el.child('div.x-tip-bd-left');
11100               //bdRight = el.child('div.x-tip-bd-right');
11101               close = el.child('div.x-tip-close');
11102               close.enableDisplayMode("block");
11103               close.on("click", hide);
11104               var d = Roo.get(document);
11105               d.on("mousedown", onDown);
11106               d.on("mouseover", onOver);
11107               d.on("mouseout", onOut);
11108               d.on("mousemove", onMove);
11109               esc = d.addKeyListener(27, hide);
11110               esc.disable();
11111               if(Roo.dd.DD){
11112                   dd = el.initDD("default", null, {
11113                       onDrag : function(){
11114                           el.sync();  
11115                       }
11116                   });
11117                   dd.setHandleElId(tipTitle.id);
11118                   dd.lock();
11119               }
11120               inited = true;
11121           }
11122           this.enable(); 
11123        },
11124
11125     /**
11126      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11127      * are supported:
11128      * <pre>
11129 Property    Type                   Description
11130 ----------  ---------------------  ------------------------------------------------------------------------
11131 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11132      * </ul>
11133      * @param {Object} config The config object
11134      */
11135        register : function(config){
11136            var cs = config instanceof Array ? config : arguments;
11137            for(var i = 0, len = cs.length; i < len; i++) {
11138                var c = cs[i];
11139                var target = c.target;
11140                if(target){
11141                    if(target instanceof Array){
11142                        for(var j = 0, jlen = target.length; j < jlen; j++){
11143                            tagEls[target[j]] = c;
11144                        }
11145                    }else{
11146                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11147                    }
11148                }
11149            }
11150        },
11151
11152     /**
11153      * Removes this quick tip from its element and destroys it.
11154      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11155      */
11156        unregister : function(el){
11157            delete tagEls[Roo.id(el)];
11158        },
11159
11160     /**
11161      * Enable this quick tip.
11162      */
11163        enable : function(){
11164            if(inited && disabled){
11165                locks.pop();
11166                if(locks.length < 1){
11167                    disabled = false;
11168                }
11169            }
11170        },
11171
11172     /**
11173      * Disable this quick tip.
11174      */
11175        disable : function(){
11176           disabled = true;
11177           clearTimeout(showProc);
11178           clearTimeout(hideProc);
11179           clearTimeout(dismissProc);
11180           if(ce){
11181               hide(true);
11182           }
11183           locks.push(1);
11184        },
11185
11186     /**
11187      * Returns true if the quick tip is enabled, else false.
11188      */
11189        isEnabled : function(){
11190             return !disabled;
11191        },
11192
11193         // private
11194        tagConfig : {
11195            namespace : "roo", // was ext?? this may break..
11196            alt_namespace : "ext",
11197            attribute : "qtip",
11198            width : "width",
11199            target : "target",
11200            title : "qtitle",
11201            hide : "hide",
11202            cls : "qclass"
11203        }
11204    };
11205 }();
11206
11207 // backwards compat
11208 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11209  * Based on:
11210  * Ext JS Library 1.1.1
11211  * Copyright(c) 2006-2007, Ext JS, LLC.
11212  *
11213  * Originally Released Under LGPL - original licence link has changed is not relivant.
11214  *
11215  * Fork - LGPL
11216  * <script type="text/javascript">
11217  */
11218  
11219
11220 /**
11221  * @class Roo.tree.TreePanel
11222  * @extends Roo.data.Tree
11223  * @cfg {Roo.tree.TreeNode} root The root node
11224  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11225  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11226  * @cfg {Boolean} enableDD true to enable drag and drop
11227  * @cfg {Boolean} enableDrag true to enable just drag
11228  * @cfg {Boolean} enableDrop true to enable just drop
11229  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11230  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11231  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11232  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11233  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11234  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11235  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11236  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11237  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11238  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11239  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11240  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
11241  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
11242  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11243  * @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>
11244  * @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>
11245  * 
11246  * @constructor
11247  * @param {String/HTMLElement/Element} el The container element
11248  * @param {Object} config
11249  */
11250 Roo.tree.TreePanel = function(el, config){
11251     var root = false;
11252     var loader = false;
11253     if (config.root) {
11254         root = config.root;
11255         delete config.root;
11256     }
11257     if (config.loader) {
11258         loader = config.loader;
11259         delete config.loader;
11260     }
11261     
11262     Roo.apply(this, config);
11263     Roo.tree.TreePanel.superclass.constructor.call(this);
11264     this.el = Roo.get(el);
11265     this.el.addClass('x-tree');
11266     //console.log(root);
11267     if (root) {
11268         this.setRootNode( Roo.factory(root, Roo.tree));
11269     }
11270     if (loader) {
11271         this.loader = Roo.factory(loader, Roo.tree);
11272     }
11273    /**
11274     * Read-only. The id of the container element becomes this TreePanel's id.
11275     */
11276     this.id = this.el.id;
11277     this.addEvents({
11278         /**
11279         * @event beforeload
11280         * Fires before a node is loaded, return false to cancel
11281         * @param {Node} node The node being loaded
11282         */
11283         "beforeload" : true,
11284         /**
11285         * @event load
11286         * Fires when a node is loaded
11287         * @param {Node} node The node that was loaded
11288         */
11289         "load" : true,
11290         /**
11291         * @event textchange
11292         * Fires when the text for a node is changed
11293         * @param {Node} node The node
11294         * @param {String} text The new text
11295         * @param {String} oldText The old text
11296         */
11297         "textchange" : true,
11298         /**
11299         * @event beforeexpand
11300         * Fires before a node is expanded, return false to cancel.
11301         * @param {Node} node The node
11302         * @param {Boolean} deep
11303         * @param {Boolean} anim
11304         */
11305         "beforeexpand" : true,
11306         /**
11307         * @event beforecollapse
11308         * Fires before a node is collapsed, return false to cancel.
11309         * @param {Node} node The node
11310         * @param {Boolean} deep
11311         * @param {Boolean} anim
11312         */
11313         "beforecollapse" : true,
11314         /**
11315         * @event expand
11316         * Fires when a node is expanded
11317         * @param {Node} node The node
11318         */
11319         "expand" : true,
11320         /**
11321         * @event disabledchange
11322         * Fires when the disabled status of a node changes
11323         * @param {Node} node The node
11324         * @param {Boolean} disabled
11325         */
11326         "disabledchange" : true,
11327         /**
11328         * @event collapse
11329         * Fires when a node is collapsed
11330         * @param {Node} node The node
11331         */
11332         "collapse" : true,
11333         /**
11334         * @event beforeclick
11335         * Fires before click processing on a node. Return false to cancel the default action.
11336         * @param {Node} node The node
11337         * @param {Roo.EventObject} e The event object
11338         */
11339         "beforeclick":true,
11340         /**
11341         * @event checkchange
11342         * Fires when a node with a checkbox's checked property changes
11343         * @param {Node} this This node
11344         * @param {Boolean} checked
11345         */
11346         "checkchange":true,
11347         /**
11348         * @event click
11349         * Fires when a node is clicked
11350         * @param {Node} node The node
11351         * @param {Roo.EventObject} e The event object
11352         */
11353         "click":true,
11354         /**
11355         * @event dblclick
11356         * Fires when a node is double clicked
11357         * @param {Node} node The node
11358         * @param {Roo.EventObject} e The event object
11359         */
11360         "dblclick":true,
11361         /**
11362         * @event contextmenu
11363         * Fires when a node is right clicked
11364         * @param {Node} node The node
11365         * @param {Roo.EventObject} e The event object
11366         */
11367         "contextmenu":true,
11368         /**
11369         * @event beforechildrenrendered
11370         * Fires right before the child nodes for a node are rendered
11371         * @param {Node} node The node
11372         */
11373         "beforechildrenrendered":true,
11374         /**
11375         * @event startdrag
11376         * Fires when a node starts being dragged
11377         * @param {Roo.tree.TreePanel} this
11378         * @param {Roo.tree.TreeNode} node
11379         * @param {event} e The raw browser event
11380         */ 
11381        "startdrag" : true,
11382        /**
11383         * @event enddrag
11384         * Fires when a drag operation is complete
11385         * @param {Roo.tree.TreePanel} this
11386         * @param {Roo.tree.TreeNode} node
11387         * @param {event} e The raw browser event
11388         */
11389        "enddrag" : true,
11390        /**
11391         * @event dragdrop
11392         * Fires when a dragged node is dropped on a valid DD target
11393         * @param {Roo.tree.TreePanel} this
11394         * @param {Roo.tree.TreeNode} node
11395         * @param {DD} dd The dd it was dropped on
11396         * @param {event} e The raw browser event
11397         */
11398        "dragdrop" : true,
11399        /**
11400         * @event beforenodedrop
11401         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11402         * passed to handlers has the following properties:<br />
11403         * <ul style="padding:5px;padding-left:16px;">
11404         * <li>tree - The TreePanel</li>
11405         * <li>target - The node being targeted for the drop</li>
11406         * <li>data - The drag data from the drag source</li>
11407         * <li>point - The point of the drop - append, above or below</li>
11408         * <li>source - The drag source</li>
11409         * <li>rawEvent - Raw mouse event</li>
11410         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11411         * to be inserted by setting them on this object.</li>
11412         * <li>cancel - Set this to true to cancel the drop.</li>
11413         * </ul>
11414         * @param {Object} dropEvent
11415         */
11416        "beforenodedrop" : true,
11417        /**
11418         * @event nodedrop
11419         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11420         * passed to handlers has the following properties:<br />
11421         * <ul style="padding:5px;padding-left:16px;">
11422         * <li>tree - The TreePanel</li>
11423         * <li>target - The node being targeted for the drop</li>
11424         * <li>data - The drag data from the drag source</li>
11425         * <li>point - The point of the drop - append, above or below</li>
11426         * <li>source - The drag source</li>
11427         * <li>rawEvent - Raw mouse event</li>
11428         * <li>dropNode - Dropped node(s).</li>
11429         * </ul>
11430         * @param {Object} dropEvent
11431         */
11432        "nodedrop" : true,
11433         /**
11434         * @event nodedragover
11435         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11436         * passed to handlers has the following properties:<br />
11437         * <ul style="padding:5px;padding-left:16px;">
11438         * <li>tree - The TreePanel</li>
11439         * <li>target - The node being targeted for the drop</li>
11440         * <li>data - The drag data from the drag source</li>
11441         * <li>point - The point of the drop - append, above or below</li>
11442         * <li>source - The drag source</li>
11443         * <li>rawEvent - Raw mouse event</li>
11444         * <li>dropNode - Drop node(s) provided by the source.</li>
11445         * <li>cancel - Set this to true to signal drop not allowed.</li>
11446         * </ul>
11447         * @param {Object} dragOverEvent
11448         */
11449        "nodedragover" : true,
11450        /**
11451         * @event appendnode
11452         * Fires when append node to the tree
11453         * @param {Roo.tree.TreePanel} this
11454         * @param {Roo.tree.TreeNode} node
11455         * @param {Number} index The index of the newly appended node
11456         */
11457        "appendnode" : true
11458         
11459     });
11460     if(this.singleExpand){
11461        this.on("beforeexpand", this.restrictExpand, this);
11462     }
11463     if (this.editor) {
11464         this.editor.tree = this;
11465         this.editor = Roo.factory(this.editor, Roo.tree);
11466     }
11467     
11468     if (this.selModel) {
11469         this.selModel = Roo.factory(this.selModel, Roo.tree);
11470     }
11471    
11472 };
11473 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11474     rootVisible : true,
11475     animate: Roo.enableFx,
11476     lines : true,
11477     enableDD : false,
11478     hlDrop : Roo.enableFx,
11479   
11480     renderer: false,
11481     
11482     rendererTip: false,
11483     // private
11484     restrictExpand : function(node){
11485         var p = node.parentNode;
11486         if(p){
11487             if(p.expandedChild && p.expandedChild.parentNode == p){
11488                 p.expandedChild.collapse();
11489             }
11490             p.expandedChild = node;
11491         }
11492     },
11493
11494     // private override
11495     setRootNode : function(node){
11496         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11497         if(!this.rootVisible){
11498             node.ui = new Roo.tree.RootTreeNodeUI(node);
11499         }
11500         return node;
11501     },
11502
11503     /**
11504      * Returns the container element for this TreePanel
11505      */
11506     getEl : function(){
11507         return this.el;
11508     },
11509
11510     /**
11511      * Returns the default TreeLoader for this TreePanel
11512      */
11513     getLoader : function(){
11514         return this.loader;
11515     },
11516
11517     /**
11518      * Expand all nodes
11519      */
11520     expandAll : function(){
11521         this.root.expand(true);
11522     },
11523
11524     /**
11525      * Collapse all nodes
11526      */
11527     collapseAll : function(){
11528         this.root.collapse(true);
11529     },
11530
11531     /**
11532      * Returns the selection model used by this TreePanel
11533      */
11534     getSelectionModel : function(){
11535         if(!this.selModel){
11536             this.selModel = new Roo.tree.DefaultSelectionModel();
11537         }
11538         return this.selModel;
11539     },
11540
11541     /**
11542      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11543      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11544      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11545      * @return {Array}
11546      */
11547     getChecked : function(a, startNode){
11548         startNode = startNode || this.root;
11549         var r = [];
11550         var f = function(){
11551             if(this.attributes.checked){
11552                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11553             }
11554         }
11555         startNode.cascade(f);
11556         return r;
11557     },
11558
11559     /**
11560      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11561      * @param {String} path
11562      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11563      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11564      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11565      */
11566     expandPath : function(path, attr, callback){
11567         attr = attr || "id";
11568         var keys = path.split(this.pathSeparator);
11569         var curNode = this.root;
11570         if(curNode.attributes[attr] != keys[1]){ // invalid root
11571             if(callback){
11572                 callback(false, null);
11573             }
11574             return;
11575         }
11576         var index = 1;
11577         var f = function(){
11578             if(++index == keys.length){
11579                 if(callback){
11580                     callback(true, curNode);
11581                 }
11582                 return;
11583             }
11584             var c = curNode.findChild(attr, keys[index]);
11585             if(!c){
11586                 if(callback){
11587                     callback(false, curNode);
11588                 }
11589                 return;
11590             }
11591             curNode = c;
11592             c.expand(false, false, f);
11593         };
11594         curNode.expand(false, false, f);
11595     },
11596
11597     /**
11598      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11599      * @param {String} path
11600      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11601      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11602      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11603      */
11604     selectPath : function(path, attr, callback){
11605         attr = attr || "id";
11606         var keys = path.split(this.pathSeparator);
11607         var v = keys.pop();
11608         if(keys.length > 0){
11609             var f = function(success, node){
11610                 if(success && node){
11611                     var n = node.findChild(attr, v);
11612                     if(n){
11613                         n.select();
11614                         if(callback){
11615                             callback(true, n);
11616                         }
11617                     }else if(callback){
11618                         callback(false, n);
11619                     }
11620                 }else{
11621                     if(callback){
11622                         callback(false, n);
11623                     }
11624                 }
11625             };
11626             this.expandPath(keys.join(this.pathSeparator), attr, f);
11627         }else{
11628             this.root.select();
11629             if(callback){
11630                 callback(true, this.root);
11631             }
11632         }
11633     },
11634
11635     getTreeEl : function(){
11636         return this.el;
11637     },
11638
11639     /**
11640      * Trigger rendering of this TreePanel
11641      */
11642     render : function(){
11643         if (this.innerCt) {
11644             return this; // stop it rendering more than once!!
11645         }
11646         
11647         this.innerCt = this.el.createChild({tag:"ul",
11648                cls:"x-tree-root-ct " +
11649                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
11650
11651         if(this.containerScroll){
11652             Roo.dd.ScrollManager.register(this.el);
11653         }
11654         if((this.enableDD || this.enableDrop) && !this.dropZone){
11655            /**
11656             * The dropZone used by this tree if drop is enabled
11657             * @type Roo.tree.TreeDropZone
11658             */
11659              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
11660                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
11661            });
11662         }
11663         if((this.enableDD || this.enableDrag) && !this.dragZone){
11664            /**
11665             * The dragZone used by this tree if drag is enabled
11666             * @type Roo.tree.TreeDragZone
11667             */
11668             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
11669                ddGroup: this.ddGroup || "TreeDD",
11670                scroll: this.ddScroll
11671            });
11672         }
11673         this.getSelectionModel().init(this);
11674         if (!this.root) {
11675             Roo.log("ROOT not set in tree");
11676             return this;
11677         }
11678         this.root.render();
11679         if(!this.rootVisible){
11680             this.root.renderChildren();
11681         }
11682         return this;
11683     }
11684 });/*
11685  * Based on:
11686  * Ext JS Library 1.1.1
11687  * Copyright(c) 2006-2007, Ext JS, LLC.
11688  *
11689  * Originally Released Under LGPL - original licence link has changed is not relivant.
11690  *
11691  * Fork - LGPL
11692  * <script type="text/javascript">
11693  */
11694  
11695
11696 /**
11697  * @class Roo.tree.DefaultSelectionModel
11698  * @extends Roo.util.Observable
11699  * The default single selection for a TreePanel.
11700  * @param {Object} cfg Configuration
11701  */
11702 Roo.tree.DefaultSelectionModel = function(cfg){
11703    this.selNode = null;
11704    
11705    
11706    
11707    this.addEvents({
11708        /**
11709         * @event selectionchange
11710         * Fires when the selected node changes
11711         * @param {DefaultSelectionModel} this
11712         * @param {TreeNode} node the new selection
11713         */
11714        "selectionchange" : true,
11715
11716        /**
11717         * @event beforeselect
11718         * Fires before the selected node changes, return false to cancel the change
11719         * @param {DefaultSelectionModel} this
11720         * @param {TreeNode} node the new selection
11721         * @param {TreeNode} node the old selection
11722         */
11723        "beforeselect" : true
11724    });
11725    
11726     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
11727 };
11728
11729 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
11730     init : function(tree){
11731         this.tree = tree;
11732         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11733         tree.on("click", this.onNodeClick, this);
11734     },
11735     
11736     onNodeClick : function(node, e){
11737         if (e.ctrlKey && this.selNode == node)  {
11738             this.unselect(node);
11739             return;
11740         }
11741         this.select(node);
11742     },
11743     
11744     /**
11745      * Select a node.
11746      * @param {TreeNode} node The node to select
11747      * @return {TreeNode} The selected node
11748      */
11749     select : function(node){
11750         var last = this.selNode;
11751         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
11752             if(last){
11753                 last.ui.onSelectedChange(false);
11754             }
11755             this.selNode = node;
11756             node.ui.onSelectedChange(true);
11757             this.fireEvent("selectionchange", this, node, last);
11758         }
11759         return node;
11760     },
11761     
11762     /**
11763      * Deselect a node.
11764      * @param {TreeNode} node The node to unselect
11765      */
11766     unselect : function(node){
11767         if(this.selNode == node){
11768             this.clearSelections();
11769         }    
11770     },
11771     
11772     /**
11773      * Clear all selections
11774      */
11775     clearSelections : function(){
11776         var n = this.selNode;
11777         if(n){
11778             n.ui.onSelectedChange(false);
11779             this.selNode = null;
11780             this.fireEvent("selectionchange", this, null);
11781         }
11782         return n;
11783     },
11784     
11785     /**
11786      * Get the selected node
11787      * @return {TreeNode} The selected node
11788      */
11789     getSelectedNode : function(){
11790         return this.selNode;    
11791     },
11792     
11793     /**
11794      * Returns true if the node is selected
11795      * @param {TreeNode} node The node to check
11796      * @return {Boolean}
11797      */
11798     isSelected : function(node){
11799         return this.selNode == node;  
11800     },
11801
11802     /**
11803      * Selects the node above the selected node in the tree, intelligently walking the nodes
11804      * @return TreeNode The new selection
11805      */
11806     selectPrevious : function(){
11807         var s = this.selNode || this.lastSelNode;
11808         if(!s){
11809             return null;
11810         }
11811         var ps = s.previousSibling;
11812         if(ps){
11813             if(!ps.isExpanded() || ps.childNodes.length < 1){
11814                 return this.select(ps);
11815             } else{
11816                 var lc = ps.lastChild;
11817                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
11818                     lc = lc.lastChild;
11819                 }
11820                 return this.select(lc);
11821             }
11822         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
11823             return this.select(s.parentNode);
11824         }
11825         return null;
11826     },
11827
11828     /**
11829      * Selects the node above the selected node in the tree, intelligently walking the nodes
11830      * @return TreeNode The new selection
11831      */
11832     selectNext : function(){
11833         var s = this.selNode || this.lastSelNode;
11834         if(!s){
11835             return null;
11836         }
11837         if(s.firstChild && s.isExpanded()){
11838              return this.select(s.firstChild);
11839          }else if(s.nextSibling){
11840              return this.select(s.nextSibling);
11841          }else if(s.parentNode){
11842             var newS = null;
11843             s.parentNode.bubble(function(){
11844                 if(this.nextSibling){
11845                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
11846                     return false;
11847                 }
11848             });
11849             return newS;
11850          }
11851         return null;
11852     },
11853
11854     onKeyDown : function(e){
11855         var s = this.selNode || this.lastSelNode;
11856         // undesirable, but required
11857         var sm = this;
11858         if(!s){
11859             return;
11860         }
11861         var k = e.getKey();
11862         switch(k){
11863              case e.DOWN:
11864                  e.stopEvent();
11865                  this.selectNext();
11866              break;
11867              case e.UP:
11868                  e.stopEvent();
11869                  this.selectPrevious();
11870              break;
11871              case e.RIGHT:
11872                  e.preventDefault();
11873                  if(s.hasChildNodes()){
11874                      if(!s.isExpanded()){
11875                          s.expand();
11876                      }else if(s.firstChild){
11877                          this.select(s.firstChild, e);
11878                      }
11879                  }
11880              break;
11881              case e.LEFT:
11882                  e.preventDefault();
11883                  if(s.hasChildNodes() && s.isExpanded()){
11884                      s.collapse();
11885                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
11886                      this.select(s.parentNode, e);
11887                  }
11888              break;
11889         };
11890     }
11891 });
11892
11893 /**
11894  * @class Roo.tree.MultiSelectionModel
11895  * @extends Roo.util.Observable
11896  * Multi selection for a TreePanel.
11897  * @param {Object} cfg Configuration
11898  */
11899 Roo.tree.MultiSelectionModel = function(){
11900    this.selNodes = [];
11901    this.selMap = {};
11902    this.addEvents({
11903        /**
11904         * @event selectionchange
11905         * Fires when the selected nodes change
11906         * @param {MultiSelectionModel} this
11907         * @param {Array} nodes Array of the selected nodes
11908         */
11909        "selectionchange" : true
11910    });
11911    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
11912    
11913 };
11914
11915 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
11916     init : function(tree){
11917         this.tree = tree;
11918         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11919         tree.on("click", this.onNodeClick, this);
11920     },
11921     
11922     onNodeClick : function(node, e){
11923         this.select(node, e, e.ctrlKey);
11924     },
11925     
11926     /**
11927      * Select a node.
11928      * @param {TreeNode} node The node to select
11929      * @param {EventObject} e (optional) An event associated with the selection
11930      * @param {Boolean} keepExisting True to retain existing selections
11931      * @return {TreeNode} The selected node
11932      */
11933     select : function(node, e, keepExisting){
11934         if(keepExisting !== true){
11935             this.clearSelections(true);
11936         }
11937         if(this.isSelected(node)){
11938             this.lastSelNode = node;
11939             return node;
11940         }
11941         this.selNodes.push(node);
11942         this.selMap[node.id] = node;
11943         this.lastSelNode = node;
11944         node.ui.onSelectedChange(true);
11945         this.fireEvent("selectionchange", this, this.selNodes);
11946         return node;
11947     },
11948     
11949     /**
11950      * Deselect a node.
11951      * @param {TreeNode} node The node to unselect
11952      */
11953     unselect : function(node){
11954         if(this.selMap[node.id]){
11955             node.ui.onSelectedChange(false);
11956             var sn = this.selNodes;
11957             var index = -1;
11958             if(sn.indexOf){
11959                 index = sn.indexOf(node);
11960             }else{
11961                 for(var i = 0, len = sn.length; i < len; i++){
11962                     if(sn[i] == node){
11963                         index = i;
11964                         break;
11965                     }
11966                 }
11967             }
11968             if(index != -1){
11969                 this.selNodes.splice(index, 1);
11970             }
11971             delete this.selMap[node.id];
11972             this.fireEvent("selectionchange", this, this.selNodes);
11973         }
11974     },
11975     
11976     /**
11977      * Clear all selections
11978      */
11979     clearSelections : function(suppressEvent){
11980         var sn = this.selNodes;
11981         if(sn.length > 0){
11982             for(var i = 0, len = sn.length; i < len; i++){
11983                 sn[i].ui.onSelectedChange(false);
11984             }
11985             this.selNodes = [];
11986             this.selMap = {};
11987             if(suppressEvent !== true){
11988                 this.fireEvent("selectionchange", this, this.selNodes);
11989             }
11990         }
11991     },
11992     
11993     /**
11994      * Returns true if the node is selected
11995      * @param {TreeNode} node The node to check
11996      * @return {Boolean}
11997      */
11998     isSelected : function(node){
11999         return this.selMap[node.id] ? true : false;  
12000     },
12001     
12002     /**
12003      * Returns an array of the selected nodes
12004      * @return {Array}
12005      */
12006     getSelectedNodes : function(){
12007         return this.selNodes;    
12008     },
12009
12010     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12011
12012     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12013
12014     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12015 });/*
12016  * Based on:
12017  * Ext JS Library 1.1.1
12018  * Copyright(c) 2006-2007, Ext JS, LLC.
12019  *
12020  * Originally Released Under LGPL - original licence link has changed is not relivant.
12021  *
12022  * Fork - LGPL
12023  * <script type="text/javascript">
12024  */
12025  
12026 /**
12027  * @class Roo.tree.TreeNode
12028  * @extends Roo.data.Node
12029  * @cfg {String} text The text for this node
12030  * @cfg {Boolean} expanded true to start the node expanded
12031  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12032  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12033  * @cfg {Boolean} disabled true to start the node disabled
12034  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12035  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12036  * @cfg {String} cls A css class to be added to the node
12037  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12038  * @cfg {String} href URL of the link used for the node (defaults to #)
12039  * @cfg {String} hrefTarget target frame for the link
12040  * @cfg {String} qtip An Ext QuickTip for the node
12041  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12042  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12043  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12044  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12045  * (defaults to undefined with no checkbox rendered)
12046  * @constructor
12047  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12048  */
12049 Roo.tree.TreeNode = function(attributes){
12050     attributes = attributes || {};
12051     if(typeof attributes == "string"){
12052         attributes = {text: attributes};
12053     }
12054     this.childrenRendered = false;
12055     this.rendered = false;
12056     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12057     this.expanded = attributes.expanded === true;
12058     this.isTarget = attributes.isTarget !== false;
12059     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12060     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12061
12062     /**
12063      * Read-only. The text for this node. To change it use setText().
12064      * @type String
12065      */
12066     this.text = attributes.text;
12067     /**
12068      * True if this node is disabled.
12069      * @type Boolean
12070      */
12071     this.disabled = attributes.disabled === true;
12072
12073     this.addEvents({
12074         /**
12075         * @event textchange
12076         * Fires when the text for this node is changed
12077         * @param {Node} this This node
12078         * @param {String} text The new text
12079         * @param {String} oldText The old text
12080         */
12081         "textchange" : true,
12082         /**
12083         * @event beforeexpand
12084         * Fires before this node is expanded, return false to cancel.
12085         * @param {Node} this This node
12086         * @param {Boolean} deep
12087         * @param {Boolean} anim
12088         */
12089         "beforeexpand" : true,
12090         /**
12091         * @event beforecollapse
12092         * Fires before this node is collapsed, return false to cancel.
12093         * @param {Node} this This node
12094         * @param {Boolean} deep
12095         * @param {Boolean} anim
12096         */
12097         "beforecollapse" : true,
12098         /**
12099         * @event expand
12100         * Fires when this node is expanded
12101         * @param {Node} this This node
12102         */
12103         "expand" : true,
12104         /**
12105         * @event disabledchange
12106         * Fires when the disabled status of this node changes
12107         * @param {Node} this This node
12108         * @param {Boolean} disabled
12109         */
12110         "disabledchange" : true,
12111         /**
12112         * @event collapse
12113         * Fires when this node is collapsed
12114         * @param {Node} this This node
12115         */
12116         "collapse" : true,
12117         /**
12118         * @event beforeclick
12119         * Fires before click processing. Return false to cancel the default action.
12120         * @param {Node} this This node
12121         * @param {Roo.EventObject} e The event object
12122         */
12123         "beforeclick":true,
12124         /**
12125         * @event checkchange
12126         * Fires when a node with a checkbox's checked property changes
12127         * @param {Node} this This node
12128         * @param {Boolean} checked
12129         */
12130         "checkchange":true,
12131         /**
12132         * @event click
12133         * Fires when this node is clicked
12134         * @param {Node} this This node
12135         * @param {Roo.EventObject} e The event object
12136         */
12137         "click":true,
12138         /**
12139         * @event dblclick
12140         * Fires when this node is double clicked
12141         * @param {Node} this This node
12142         * @param {Roo.EventObject} e The event object
12143         */
12144         "dblclick":true,
12145         /**
12146         * @event contextmenu
12147         * Fires when this node is right clicked
12148         * @param {Node} this This node
12149         * @param {Roo.EventObject} e The event object
12150         */
12151         "contextmenu":true,
12152         /**
12153         * @event beforechildrenrendered
12154         * Fires right before the child nodes for this node are rendered
12155         * @param {Node} this This node
12156         */
12157         "beforechildrenrendered":true
12158     });
12159
12160     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12161
12162     /**
12163      * Read-only. The UI for this node
12164      * @type TreeNodeUI
12165      */
12166     this.ui = new uiClass(this);
12167     
12168     // finally support items[]
12169     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12170         return;
12171     }
12172     
12173     
12174     Roo.each(this.attributes.items, function(c) {
12175         this.appendChild(Roo.factory(c,Roo.Tree));
12176     }, this);
12177     delete this.attributes.items;
12178     
12179     
12180     
12181 };
12182 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12183     preventHScroll: true,
12184     /**
12185      * Returns true if this node is expanded
12186      * @return {Boolean}
12187      */
12188     isExpanded : function(){
12189         return this.expanded;
12190     },
12191
12192     /**
12193      * Returns the UI object for this node
12194      * @return {TreeNodeUI}
12195      */
12196     getUI : function(){
12197         return this.ui;
12198     },
12199
12200     // private override
12201     setFirstChild : function(node){
12202         var of = this.firstChild;
12203         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12204         if(this.childrenRendered && of && node != of){
12205             of.renderIndent(true, true);
12206         }
12207         if(this.rendered){
12208             this.renderIndent(true, true);
12209         }
12210     },
12211
12212     // private override
12213     setLastChild : function(node){
12214         var ol = this.lastChild;
12215         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12216         if(this.childrenRendered && ol && node != ol){
12217             ol.renderIndent(true, true);
12218         }
12219         if(this.rendered){
12220             this.renderIndent(true, true);
12221         }
12222     },
12223
12224     // these methods are overridden to provide lazy rendering support
12225     // private override
12226     appendChild : function()
12227     {
12228         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12229         if(node && this.childrenRendered){
12230             node.render();
12231         }
12232         this.ui.updateExpandIcon();
12233         return node;
12234     },
12235
12236     // private override
12237     removeChild : function(node){
12238         this.ownerTree.getSelectionModel().unselect(node);
12239         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12240         // if it's been rendered remove dom node
12241         if(this.childrenRendered){
12242             node.ui.remove();
12243         }
12244         if(this.childNodes.length < 1){
12245             this.collapse(false, false);
12246         }else{
12247             this.ui.updateExpandIcon();
12248         }
12249         if(!this.firstChild) {
12250             this.childrenRendered = false;
12251         }
12252         return node;
12253     },
12254
12255     // private override
12256     insertBefore : function(node, refNode){
12257         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12258         if(newNode && refNode && this.childrenRendered){
12259             node.render();
12260         }
12261         this.ui.updateExpandIcon();
12262         return newNode;
12263     },
12264
12265     /**
12266      * Sets the text for this node
12267      * @param {String} text
12268      */
12269     setText : function(text){
12270         var oldText = this.text;
12271         this.text = text;
12272         this.attributes.text = text;
12273         if(this.rendered){ // event without subscribing
12274             this.ui.onTextChange(this, text, oldText);
12275         }
12276         this.fireEvent("textchange", this, text, oldText);
12277     },
12278
12279     /**
12280      * Triggers selection of this node
12281      */
12282     select : function(){
12283         this.getOwnerTree().getSelectionModel().select(this);
12284     },
12285
12286     /**
12287      * Triggers deselection of this node
12288      */
12289     unselect : function(){
12290         this.getOwnerTree().getSelectionModel().unselect(this);
12291     },
12292
12293     /**
12294      * Returns true if this node is selected
12295      * @return {Boolean}
12296      */
12297     isSelected : function(){
12298         return this.getOwnerTree().getSelectionModel().isSelected(this);
12299     },
12300
12301     /**
12302      * Expand this node.
12303      * @param {Boolean} deep (optional) True to expand all children as well
12304      * @param {Boolean} anim (optional) false to cancel the default animation
12305      * @param {Function} callback (optional) A callback to be called when
12306      * expanding this node completes (does not wait for deep expand to complete).
12307      * Called with 1 parameter, this node.
12308      */
12309     expand : function(deep, anim, callback){
12310         if(!this.expanded){
12311             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12312                 return;
12313             }
12314             if(!this.childrenRendered){
12315                 this.renderChildren();
12316             }
12317             this.expanded = true;
12318             
12319             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12320                 this.ui.animExpand(function(){
12321                     this.fireEvent("expand", this);
12322                     if(typeof callback == "function"){
12323                         callback(this);
12324                     }
12325                     if(deep === true){
12326                         this.expandChildNodes(true);
12327                     }
12328                 }.createDelegate(this));
12329                 return;
12330             }else{
12331                 this.ui.expand();
12332                 this.fireEvent("expand", this);
12333                 if(typeof callback == "function"){
12334                     callback(this);
12335                 }
12336             }
12337         }else{
12338            if(typeof callback == "function"){
12339                callback(this);
12340            }
12341         }
12342         if(deep === true){
12343             this.expandChildNodes(true);
12344         }
12345     },
12346
12347     isHiddenRoot : function(){
12348         return this.isRoot && !this.getOwnerTree().rootVisible;
12349     },
12350
12351     /**
12352      * Collapse this node.
12353      * @param {Boolean} deep (optional) True to collapse all children as well
12354      * @param {Boolean} anim (optional) false to cancel the default animation
12355      */
12356     collapse : function(deep, anim){
12357         if(this.expanded && !this.isHiddenRoot()){
12358             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12359                 return;
12360             }
12361             this.expanded = false;
12362             if((this.getOwnerTree().animate && anim !== false) || anim){
12363                 this.ui.animCollapse(function(){
12364                     this.fireEvent("collapse", this);
12365                     if(deep === true){
12366                         this.collapseChildNodes(true);
12367                     }
12368                 }.createDelegate(this));
12369                 return;
12370             }else{
12371                 this.ui.collapse();
12372                 this.fireEvent("collapse", this);
12373             }
12374         }
12375         if(deep === true){
12376             var cs = this.childNodes;
12377             for(var i = 0, len = cs.length; i < len; i++) {
12378                 cs[i].collapse(true, false);
12379             }
12380         }
12381     },
12382
12383     // private
12384     delayedExpand : function(delay){
12385         if(!this.expandProcId){
12386             this.expandProcId = this.expand.defer(delay, this);
12387         }
12388     },
12389
12390     // private
12391     cancelExpand : function(){
12392         if(this.expandProcId){
12393             clearTimeout(this.expandProcId);
12394         }
12395         this.expandProcId = false;
12396     },
12397
12398     /**
12399      * Toggles expanded/collapsed state of the node
12400      */
12401     toggle : function(){
12402         if(this.expanded){
12403             this.collapse();
12404         }else{
12405             this.expand();
12406         }
12407     },
12408
12409     /**
12410      * Ensures all parent nodes are expanded
12411      */
12412     ensureVisible : function(callback){
12413         var tree = this.getOwnerTree();
12414         tree.expandPath(this.parentNode.getPath(), false, function(){
12415             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12416             Roo.callback(callback);
12417         }.createDelegate(this));
12418     },
12419
12420     /**
12421      * Expand all child nodes
12422      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12423      */
12424     expandChildNodes : function(deep){
12425         var cs = this.childNodes;
12426         for(var i = 0, len = cs.length; i < len; i++) {
12427                 cs[i].expand(deep);
12428         }
12429     },
12430
12431     /**
12432      * Collapse all child nodes
12433      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12434      */
12435     collapseChildNodes : function(deep){
12436         var cs = this.childNodes;
12437         for(var i = 0, len = cs.length; i < len; i++) {
12438                 cs[i].collapse(deep);
12439         }
12440     },
12441
12442     /**
12443      * Disables this node
12444      */
12445     disable : function(){
12446         this.disabled = true;
12447         this.unselect();
12448         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12449             this.ui.onDisableChange(this, true);
12450         }
12451         this.fireEvent("disabledchange", this, true);
12452     },
12453
12454     /**
12455      * Enables this node
12456      */
12457     enable : function(){
12458         this.disabled = false;
12459         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12460             this.ui.onDisableChange(this, false);
12461         }
12462         this.fireEvent("disabledchange", this, false);
12463     },
12464
12465     // private
12466     renderChildren : function(suppressEvent){
12467         if(suppressEvent !== false){
12468             this.fireEvent("beforechildrenrendered", this);
12469         }
12470         var cs = this.childNodes;
12471         for(var i = 0, len = cs.length; i < len; i++){
12472             cs[i].render(true);
12473         }
12474         this.childrenRendered = true;
12475     },
12476
12477     // private
12478     sort : function(fn, scope){
12479         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12480         if(this.childrenRendered){
12481             var cs = this.childNodes;
12482             for(var i = 0, len = cs.length; i < len; i++){
12483                 cs[i].render(true);
12484             }
12485         }
12486     },
12487
12488     // private
12489     render : function(bulkRender){
12490         this.ui.render(bulkRender);
12491         if(!this.rendered){
12492             this.rendered = true;
12493             if(this.expanded){
12494                 this.expanded = false;
12495                 this.expand(false, false);
12496             }
12497         }
12498     },
12499
12500     // private
12501     renderIndent : function(deep, refresh){
12502         if(refresh){
12503             this.ui.childIndent = null;
12504         }
12505         this.ui.renderIndent();
12506         if(deep === true && this.childrenRendered){
12507             var cs = this.childNodes;
12508             for(var i = 0, len = cs.length; i < len; i++){
12509                 cs[i].renderIndent(true, refresh);
12510             }
12511         }
12512     }
12513 });/*
12514  * Based on:
12515  * Ext JS Library 1.1.1
12516  * Copyright(c) 2006-2007, Ext JS, LLC.
12517  *
12518  * Originally Released Under LGPL - original licence link has changed is not relivant.
12519  *
12520  * Fork - LGPL
12521  * <script type="text/javascript">
12522  */
12523  
12524 /**
12525  * @class Roo.tree.AsyncTreeNode
12526  * @extends Roo.tree.TreeNode
12527  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12528  * @constructor
12529  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12530  */
12531  Roo.tree.AsyncTreeNode = function(config){
12532     this.loaded = false;
12533     this.loading = false;
12534     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12535     /**
12536     * @event beforeload
12537     * Fires before this node is loaded, return false to cancel
12538     * @param {Node} this This node
12539     */
12540     this.addEvents({'beforeload':true, 'load': true});
12541     /**
12542     * @event load
12543     * Fires when this node is loaded
12544     * @param {Node} this This node
12545     */
12546     /**
12547      * The loader used by this node (defaults to using the tree's defined loader)
12548      * @type TreeLoader
12549      * @property loader
12550      */
12551 };
12552 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12553     expand : function(deep, anim, callback){
12554         if(this.loading){ // if an async load is already running, waiting til it's done
12555             var timer;
12556             var f = function(){
12557                 if(!this.loading){ // done loading
12558                     clearInterval(timer);
12559                     this.expand(deep, anim, callback);
12560                 }
12561             }.createDelegate(this);
12562             timer = setInterval(f, 200);
12563             return;
12564         }
12565         if(!this.loaded){
12566             if(this.fireEvent("beforeload", this) === false){
12567                 return;
12568             }
12569             this.loading = true;
12570             this.ui.beforeLoad(this);
12571             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12572             if(loader){
12573                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12574                 return;
12575             }
12576         }
12577         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12578     },
12579     
12580     /**
12581      * Returns true if this node is currently loading
12582      * @return {Boolean}
12583      */
12584     isLoading : function(){
12585         return this.loading;  
12586     },
12587     
12588     loadComplete : function(deep, anim, callback){
12589         this.loading = false;
12590         this.loaded = true;
12591         this.ui.afterLoad(this);
12592         this.fireEvent("load", this);
12593         this.expand(deep, anim, callback);
12594     },
12595     
12596     /**
12597      * Returns true if this node has been loaded
12598      * @return {Boolean}
12599      */
12600     isLoaded : function(){
12601         return this.loaded;
12602     },
12603     
12604     hasChildNodes : function(){
12605         if(!this.isLeaf() && !this.loaded){
12606             return true;
12607         }else{
12608             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12609         }
12610     },
12611
12612     /**
12613      * Trigger a reload for this node
12614      * @param {Function} callback
12615      */
12616     reload : function(callback){
12617         this.collapse(false, false);
12618         while(this.firstChild){
12619             this.removeChild(this.firstChild);
12620         }
12621         this.childrenRendered = false;
12622         this.loaded = false;
12623         if(this.isHiddenRoot()){
12624             this.expanded = false;
12625         }
12626         this.expand(false, false, callback);
12627     }
12628 });/*
12629  * Based on:
12630  * Ext JS Library 1.1.1
12631  * Copyright(c) 2006-2007, Ext JS, LLC.
12632  *
12633  * Originally Released Under LGPL - original licence link has changed is not relivant.
12634  *
12635  * Fork - LGPL
12636  * <script type="text/javascript">
12637  */
12638  
12639 /**
12640  * @class Roo.tree.TreeNodeUI
12641  * @constructor
12642  * @param {Object} node The node to render
12643  * The TreeNode UI implementation is separate from the
12644  * tree implementation. Unless you are customizing the tree UI,
12645  * you should never have to use this directly.
12646  */
12647 Roo.tree.TreeNodeUI = function(node){
12648     this.node = node;
12649     this.rendered = false;
12650     this.animating = false;
12651     this.emptyIcon = Roo.BLANK_IMAGE_URL;
12652 };
12653
12654 Roo.tree.TreeNodeUI.prototype = {
12655     removeChild : function(node){
12656         if(this.rendered){
12657             this.ctNode.removeChild(node.ui.getEl());
12658         }
12659     },
12660
12661     beforeLoad : function(){
12662          this.addClass("x-tree-node-loading");
12663     },
12664
12665     afterLoad : function(){
12666          this.removeClass("x-tree-node-loading");
12667     },
12668
12669     onTextChange : function(node, text, oldText){
12670         if(this.rendered){
12671             this.textNode.innerHTML = text;
12672         }
12673     },
12674
12675     onDisableChange : function(node, state){
12676         this.disabled = state;
12677         if(state){
12678             this.addClass("x-tree-node-disabled");
12679         }else{
12680             this.removeClass("x-tree-node-disabled");
12681         }
12682     },
12683
12684     onSelectedChange : function(state){
12685         if(state){
12686             this.focus();
12687             this.addClass("x-tree-selected");
12688         }else{
12689             //this.blur();
12690             this.removeClass("x-tree-selected");
12691         }
12692     },
12693
12694     onMove : function(tree, node, oldParent, newParent, index, refNode){
12695         this.childIndent = null;
12696         if(this.rendered){
12697             var targetNode = newParent.ui.getContainer();
12698             if(!targetNode){//target not rendered
12699                 this.holder = document.createElement("div");
12700                 this.holder.appendChild(this.wrap);
12701                 return;
12702             }
12703             var insertBefore = refNode ? refNode.ui.getEl() : null;
12704             if(insertBefore){
12705                 targetNode.insertBefore(this.wrap, insertBefore);
12706             }else{
12707                 targetNode.appendChild(this.wrap);
12708             }
12709             this.node.renderIndent(true);
12710         }
12711     },
12712
12713     addClass : function(cls){
12714         if(this.elNode){
12715             Roo.fly(this.elNode).addClass(cls);
12716         }
12717     },
12718
12719     removeClass : function(cls){
12720         if(this.elNode){
12721             Roo.fly(this.elNode).removeClass(cls);
12722         }
12723     },
12724
12725     remove : function(){
12726         if(this.rendered){
12727             this.holder = document.createElement("div");
12728             this.holder.appendChild(this.wrap);
12729         }
12730     },
12731
12732     fireEvent : function(){
12733         return this.node.fireEvent.apply(this.node, arguments);
12734     },
12735
12736     initEvents : function(){
12737         this.node.on("move", this.onMove, this);
12738         var E = Roo.EventManager;
12739         var a = this.anchor;
12740
12741         var el = Roo.fly(a, '_treeui');
12742
12743         if(Roo.isOpera){ // opera render bug ignores the CSS
12744             el.setStyle("text-decoration", "none");
12745         }
12746
12747         el.on("click", this.onClick, this);
12748         el.on("dblclick", this.onDblClick, this);
12749
12750         if(this.checkbox){
12751             Roo.EventManager.on(this.checkbox,
12752                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
12753         }
12754
12755         el.on("contextmenu", this.onContextMenu, this);
12756
12757         var icon = Roo.fly(this.iconNode);
12758         icon.on("click", this.onClick, this);
12759         icon.on("dblclick", this.onDblClick, this);
12760         icon.on("contextmenu", this.onContextMenu, this);
12761         E.on(this.ecNode, "click", this.ecClick, this, true);
12762
12763         if(this.node.disabled){
12764             this.addClass("x-tree-node-disabled");
12765         }
12766         if(this.node.hidden){
12767             this.addClass("x-tree-node-disabled");
12768         }
12769         var ot = this.node.getOwnerTree();
12770         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
12771         if(dd && (!this.node.isRoot || ot.rootVisible)){
12772             Roo.dd.Registry.register(this.elNode, {
12773                 node: this.node,
12774                 handles: this.getDDHandles(),
12775                 isHandle: false
12776             });
12777         }
12778     },
12779
12780     getDDHandles : function(){
12781         return [this.iconNode, this.textNode];
12782     },
12783
12784     hide : function(){
12785         if(this.rendered){
12786             this.wrap.style.display = "none";
12787         }
12788     },
12789
12790     show : function(){
12791         if(this.rendered){
12792             this.wrap.style.display = "";
12793         }
12794     },
12795
12796     onContextMenu : function(e){
12797         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
12798             e.preventDefault();
12799             this.focus();
12800             this.fireEvent("contextmenu", this.node, e);
12801         }
12802     },
12803
12804     onClick : function(e){
12805         if(this.dropping){
12806             e.stopEvent();
12807             return;
12808         }
12809         if(this.fireEvent("beforeclick", this.node, e) !== false){
12810             if(!this.disabled && this.node.attributes.href){
12811                 this.fireEvent("click", this.node, e);
12812                 return;
12813             }
12814             e.preventDefault();
12815             if(this.disabled){
12816                 return;
12817             }
12818
12819             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
12820                 this.node.toggle();
12821             }
12822
12823             this.fireEvent("click", this.node, e);
12824         }else{
12825             e.stopEvent();
12826         }
12827     },
12828
12829     onDblClick : function(e){
12830         e.preventDefault();
12831         if(this.disabled){
12832             return;
12833         }
12834         if(this.checkbox){
12835             this.toggleCheck();
12836         }
12837         if(!this.animating && this.node.hasChildNodes()){
12838             this.node.toggle();
12839         }
12840         this.fireEvent("dblclick", this.node, e);
12841     },
12842
12843     onCheckChange : function(){
12844         var checked = this.checkbox.checked;
12845         this.node.attributes.checked = checked;
12846         this.fireEvent('checkchange', this.node, checked);
12847     },
12848
12849     ecClick : function(e){
12850         if(!this.animating && this.node.hasChildNodes()){
12851             this.node.toggle();
12852         }
12853     },
12854
12855     startDrop : function(){
12856         this.dropping = true;
12857     },
12858
12859     // delayed drop so the click event doesn't get fired on a drop
12860     endDrop : function(){
12861        setTimeout(function(){
12862            this.dropping = false;
12863        }.createDelegate(this), 50);
12864     },
12865
12866     expand : function(){
12867         this.updateExpandIcon();
12868         this.ctNode.style.display = "";
12869     },
12870
12871     focus : function(){
12872         if(!this.node.preventHScroll){
12873             try{this.anchor.focus();
12874             }catch(e){}
12875         }else if(!Roo.isIE){
12876             try{
12877                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
12878                 var l = noscroll.scrollLeft;
12879                 this.anchor.focus();
12880                 noscroll.scrollLeft = l;
12881             }catch(e){}
12882         }
12883     },
12884
12885     toggleCheck : function(value){
12886         var cb = this.checkbox;
12887         if(cb){
12888             cb.checked = (value === undefined ? !cb.checked : value);
12889         }
12890     },
12891
12892     blur : function(){
12893         try{
12894             this.anchor.blur();
12895         }catch(e){}
12896     },
12897
12898     animExpand : function(callback){
12899         var ct = Roo.get(this.ctNode);
12900         ct.stopFx();
12901         if(!this.node.hasChildNodes()){
12902             this.updateExpandIcon();
12903             this.ctNode.style.display = "";
12904             Roo.callback(callback);
12905             return;
12906         }
12907         this.animating = true;
12908         this.updateExpandIcon();
12909
12910         ct.slideIn('t', {
12911            callback : function(){
12912                this.animating = false;
12913                Roo.callback(callback);
12914             },
12915             scope: this,
12916             duration: this.node.ownerTree.duration || .25
12917         });
12918     },
12919
12920     highlight : function(){
12921         var tree = this.node.getOwnerTree();
12922         Roo.fly(this.wrap).highlight(
12923             tree.hlColor || "C3DAF9",
12924             {endColor: tree.hlBaseColor}
12925         );
12926     },
12927
12928     collapse : function(){
12929         this.updateExpandIcon();
12930         this.ctNode.style.display = "none";
12931     },
12932
12933     animCollapse : function(callback){
12934         var ct = Roo.get(this.ctNode);
12935         ct.enableDisplayMode('block');
12936         ct.stopFx();
12937
12938         this.animating = true;
12939         this.updateExpandIcon();
12940
12941         ct.slideOut('t', {
12942             callback : function(){
12943                this.animating = false;
12944                Roo.callback(callback);
12945             },
12946             scope: this,
12947             duration: this.node.ownerTree.duration || .25
12948         });
12949     },
12950
12951     getContainer : function(){
12952         return this.ctNode;
12953     },
12954
12955     getEl : function(){
12956         return this.wrap;
12957     },
12958
12959     appendDDGhost : function(ghostNode){
12960         ghostNode.appendChild(this.elNode.cloneNode(true));
12961     },
12962
12963     getDDRepairXY : function(){
12964         return Roo.lib.Dom.getXY(this.iconNode);
12965     },
12966
12967     onRender : function(){
12968         this.render();
12969     },
12970
12971     render : function(bulkRender){
12972         var n = this.node, a = n.attributes;
12973         var targetNode = n.parentNode ?
12974               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
12975
12976         if(!this.rendered){
12977             this.rendered = true;
12978
12979             this.renderElements(n, a, targetNode, bulkRender);
12980
12981             if(a.qtip){
12982                if(this.textNode.setAttributeNS){
12983                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
12984                    if(a.qtipTitle){
12985                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
12986                    }
12987                }else{
12988                    this.textNode.setAttribute("ext:qtip", a.qtip);
12989                    if(a.qtipTitle){
12990                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
12991                    }
12992                }
12993             }else if(a.qtipCfg){
12994                 a.qtipCfg.target = Roo.id(this.textNode);
12995                 Roo.QuickTips.register(a.qtipCfg);
12996             }
12997             this.initEvents();
12998             if(!this.node.expanded){
12999                 this.updateExpandIcon();
13000             }
13001         }else{
13002             if(bulkRender === true) {
13003                 targetNode.appendChild(this.wrap);
13004             }
13005         }
13006     },
13007
13008     renderElements : function(n, a, targetNode, bulkRender)
13009     {
13010         // add some indent caching, this helps performance when rendering a large tree
13011         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13012         var t = n.getOwnerTree();
13013         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13014         if (typeof(n.attributes.html) != 'undefined') {
13015             txt = n.attributes.html;
13016         }
13017         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
13018         var cb = typeof a.checked == 'boolean';
13019         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13020         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13021             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13022             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13023             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13024             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13025             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13026              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13027                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13028             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13029             "</li>"];
13030
13031         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13032             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13033                                 n.nextSibling.ui.getEl(), buf.join(""));
13034         }else{
13035             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13036         }
13037
13038         this.elNode = this.wrap.childNodes[0];
13039         this.ctNode = this.wrap.childNodes[1];
13040         var cs = this.elNode.childNodes;
13041         this.indentNode = cs[0];
13042         this.ecNode = cs[1];
13043         this.iconNode = cs[2];
13044         var index = 3;
13045         if(cb){
13046             this.checkbox = cs[3];
13047             index++;
13048         }
13049         this.anchor = cs[index];
13050         this.textNode = cs[index].firstChild;
13051     },
13052
13053     getAnchor : function(){
13054         return this.anchor;
13055     },
13056
13057     getTextEl : function(){
13058         return this.textNode;
13059     },
13060
13061     getIconEl : function(){
13062         return this.iconNode;
13063     },
13064
13065     isChecked : function(){
13066         return this.checkbox ? this.checkbox.checked : false;
13067     },
13068
13069     updateExpandIcon : function(){
13070         if(this.rendered){
13071             var n = this.node, c1, c2;
13072             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13073             var hasChild = n.hasChildNodes();
13074             if(hasChild){
13075                 if(n.expanded){
13076                     cls += "-minus";
13077                     c1 = "x-tree-node-collapsed";
13078                     c2 = "x-tree-node-expanded";
13079                 }else{
13080                     cls += "-plus";
13081                     c1 = "x-tree-node-expanded";
13082                     c2 = "x-tree-node-collapsed";
13083                 }
13084                 if(this.wasLeaf){
13085                     this.removeClass("x-tree-node-leaf");
13086                     this.wasLeaf = false;
13087                 }
13088                 if(this.c1 != c1 || this.c2 != c2){
13089                     Roo.fly(this.elNode).replaceClass(c1, c2);
13090                     this.c1 = c1; this.c2 = c2;
13091                 }
13092             }else{
13093                 // this changes non-leafs into leafs if they have no children.
13094                 // it's not very rational behaviour..
13095                 
13096                 if(!this.wasLeaf && this.node.leaf){
13097                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13098                     delete this.c1;
13099                     delete this.c2;
13100                     this.wasLeaf = true;
13101                 }
13102             }
13103             var ecc = "x-tree-ec-icon "+cls;
13104             if(this.ecc != ecc){
13105                 this.ecNode.className = ecc;
13106                 this.ecc = ecc;
13107             }
13108         }
13109     },
13110
13111     getChildIndent : function(){
13112         if(!this.childIndent){
13113             var buf = [];
13114             var p = this.node;
13115             while(p){
13116                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13117                     if(!p.isLast()) {
13118                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13119                     } else {
13120                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13121                     }
13122                 }
13123                 p = p.parentNode;
13124             }
13125             this.childIndent = buf.join("");
13126         }
13127         return this.childIndent;
13128     },
13129
13130     renderIndent : function(){
13131         if(this.rendered){
13132             var indent = "";
13133             var p = this.node.parentNode;
13134             if(p){
13135                 indent = p.ui.getChildIndent();
13136             }
13137             if(this.indentMarkup != indent){ // don't rerender if not required
13138                 this.indentNode.innerHTML = indent;
13139                 this.indentMarkup = indent;
13140             }
13141             this.updateExpandIcon();
13142         }
13143     }
13144 };
13145
13146 Roo.tree.RootTreeNodeUI = function(){
13147     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13148 };
13149 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13150     render : function(){
13151         if(!this.rendered){
13152             var targetNode = this.node.ownerTree.innerCt.dom;
13153             this.node.expanded = true;
13154             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13155             this.wrap = this.ctNode = targetNode.firstChild;
13156         }
13157     },
13158     collapse : function(){
13159     },
13160     expand : function(){
13161     }
13162 });/*
13163  * Based on:
13164  * Ext JS Library 1.1.1
13165  * Copyright(c) 2006-2007, Ext JS, LLC.
13166  *
13167  * Originally Released Under LGPL - original licence link has changed is not relivant.
13168  *
13169  * Fork - LGPL
13170  * <script type="text/javascript">
13171  */
13172 /**
13173  * @class Roo.tree.TreeLoader
13174  * @extends Roo.util.Observable
13175  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13176  * nodes from a specified URL. The response must be a javascript Array definition
13177  * who's elements are node definition objects. eg:
13178  * <pre><code>
13179 {  success : true,
13180    data :      [
13181    
13182     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13183     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13184     ]
13185 }
13186
13187
13188 </code></pre>
13189  * <br><br>
13190  * The old style respose with just an array is still supported, but not recommended.
13191  * <br><br>
13192  *
13193  * A server request is sent, and child nodes are loaded only when a node is expanded.
13194  * The loading node's id is passed to the server under the parameter name "node" to
13195  * enable the server to produce the correct child nodes.
13196  * <br><br>
13197  * To pass extra parameters, an event handler may be attached to the "beforeload"
13198  * event, and the parameters specified in the TreeLoader's baseParams property:
13199  * <pre><code>
13200     myTreeLoader.on("beforeload", function(treeLoader, node) {
13201         this.baseParams.category = node.attributes.category;
13202     }, this);
13203     
13204 </code></pre>
13205  *
13206  * This would pass an HTTP parameter called "category" to the server containing
13207  * the value of the Node's "category" attribute.
13208  * @constructor
13209  * Creates a new Treeloader.
13210  * @param {Object} config A config object containing config properties.
13211  */
13212 Roo.tree.TreeLoader = function(config){
13213     this.baseParams = {};
13214     this.requestMethod = "POST";
13215     Roo.apply(this, config);
13216
13217     this.addEvents({
13218     
13219         /**
13220          * @event beforeload
13221          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13222          * @param {Object} This TreeLoader object.
13223          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13224          * @param {Object} callback The callback function specified in the {@link #load} call.
13225          */
13226         beforeload : true,
13227         /**
13228          * @event load
13229          * Fires when the node has been successfuly loaded.
13230          * @param {Object} This TreeLoader object.
13231          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13232          * @param {Object} response The response object containing the data from the server.
13233          */
13234         load : true,
13235         /**
13236          * @event loadexception
13237          * Fires if the network request failed.
13238          * @param {Object} This TreeLoader object.
13239          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13240          * @param {Object} response The response object containing the data from the server.
13241          */
13242         loadexception : true,
13243         /**
13244          * @event create
13245          * Fires before a node is created, enabling you to return custom Node types 
13246          * @param {Object} This TreeLoader object.
13247          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13248          */
13249         create : true
13250     });
13251
13252     Roo.tree.TreeLoader.superclass.constructor.call(this);
13253 };
13254
13255 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13256     /**
13257     * @cfg {String} dataUrl The URL from which to request a Json string which
13258     * specifies an array of node definition object representing the child nodes
13259     * to be loaded.
13260     */
13261     /**
13262     * @cfg {String} requestMethod either GET or POST
13263     * defaults to POST (due to BC)
13264     * to be loaded.
13265     */
13266     /**
13267     * @cfg {Object} baseParams (optional) An object containing properties which
13268     * specify HTTP parameters to be passed to each request for child nodes.
13269     */
13270     /**
13271     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13272     * created by this loader. If the attributes sent by the server have an attribute in this object,
13273     * they take priority.
13274     */
13275     /**
13276     * @cfg {Object} uiProviders (optional) An object containing properties which
13277     * 
13278     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13279     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13280     * <i>uiProvider</i> attribute of a returned child node is a string rather
13281     * than a reference to a TreeNodeUI implementation, this that string value
13282     * is used as a property name in the uiProviders object. You can define the provider named
13283     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13284     */
13285     uiProviders : {},
13286
13287     /**
13288     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13289     * child nodes before loading.
13290     */
13291     clearOnLoad : true,
13292
13293     /**
13294     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13295     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13296     * Grid query { data : [ .....] }
13297     */
13298     
13299     root : false,
13300      /**
13301     * @cfg {String} queryParam (optional) 
13302     * Name of the query as it will be passed on the querystring (defaults to 'node')
13303     * eg. the request will be ?node=[id]
13304     */
13305     
13306     
13307     queryParam: false,
13308     
13309     /**
13310      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13311      * This is called automatically when a node is expanded, but may be used to reload
13312      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13313      * @param {Roo.tree.TreeNode} node
13314      * @param {Function} callback
13315      */
13316     load : function(node, callback){
13317         if(this.clearOnLoad){
13318             while(node.firstChild){
13319                 node.removeChild(node.firstChild);
13320             }
13321         }
13322         if(node.attributes.children){ // preloaded json children
13323             var cs = node.attributes.children;
13324             for(var i = 0, len = cs.length; i < len; i++){
13325                 node.appendChild(this.createNode(cs[i]));
13326             }
13327             if(typeof callback == "function"){
13328                 callback();
13329             }
13330         }else if(this.dataUrl){
13331             this.requestData(node, callback);
13332         }
13333     },
13334
13335     getParams: function(node){
13336         var buf = [], bp = this.baseParams;
13337         for(var key in bp){
13338             if(typeof bp[key] != "function"){
13339                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13340             }
13341         }
13342         var n = this.queryParam === false ? 'node' : this.queryParam;
13343         buf.push(n + "=", encodeURIComponent(node.id));
13344         return buf.join("");
13345     },
13346
13347     requestData : function(node, callback){
13348         if(this.fireEvent("beforeload", this, node, callback) !== false){
13349             this.transId = Roo.Ajax.request({
13350                 method:this.requestMethod,
13351                 url: this.dataUrl||this.url,
13352                 success: this.handleResponse,
13353                 failure: this.handleFailure,
13354                 scope: this,
13355                 argument: {callback: callback, node: node},
13356                 params: this.getParams(node)
13357             });
13358         }else{
13359             // if the load is cancelled, make sure we notify
13360             // the node that we are done
13361             if(typeof callback == "function"){
13362                 callback();
13363             }
13364         }
13365     },
13366
13367     isLoading : function(){
13368         return this.transId ? true : false;
13369     },
13370
13371     abort : function(){
13372         if(this.isLoading()){
13373             Roo.Ajax.abort(this.transId);
13374         }
13375     },
13376
13377     // private
13378     createNode : function(attr)
13379     {
13380         // apply baseAttrs, nice idea Corey!
13381         if(this.baseAttrs){
13382             Roo.applyIf(attr, this.baseAttrs);
13383         }
13384         if(this.applyLoader !== false){
13385             attr.loader = this;
13386         }
13387         // uiProvider = depreciated..
13388         
13389         if(typeof(attr.uiProvider) == 'string'){
13390            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13391                 /**  eval:var:attr */ eval(attr.uiProvider);
13392         }
13393         if(typeof(this.uiProviders['default']) != 'undefined') {
13394             attr.uiProvider = this.uiProviders['default'];
13395         }
13396         
13397         this.fireEvent('create', this, attr);
13398         
13399         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13400         return(attr.leaf ?
13401                         new Roo.tree.TreeNode(attr) :
13402                         new Roo.tree.AsyncTreeNode(attr));
13403     },
13404
13405     processResponse : function(response, node, callback)
13406     {
13407         var json = response.responseText;
13408         try {
13409             
13410             var o = Roo.decode(json);
13411             
13412             if (this.root === false && typeof(o.success) != undefined) {
13413                 this.root = 'data'; // the default behaviour for list like data..
13414                 }
13415                 
13416             if (this.root !== false &&  !o.success) {
13417                 // it's a failure condition.
13418                 var a = response.argument;
13419                 this.fireEvent("loadexception", this, a.node, response);
13420                 Roo.log("Load failed - should have a handler really");
13421                 return;
13422             }
13423             
13424             
13425             
13426             if (this.root !== false) {
13427                  o = o[this.root];
13428             }
13429             
13430             for(var i = 0, len = o.length; i < len; i++){
13431                 var n = this.createNode(o[i]);
13432                 if(n){
13433                     node.appendChild(n);
13434                 }
13435             }
13436             if(typeof callback == "function"){
13437                 callback(this, node);
13438             }
13439         }catch(e){
13440             this.handleFailure(response);
13441         }
13442     },
13443
13444     handleResponse : function(response){
13445         this.transId = false;
13446         var a = response.argument;
13447         this.processResponse(response, a.node, a.callback);
13448         this.fireEvent("load", this, a.node, response);
13449     },
13450
13451     handleFailure : function(response)
13452     {
13453         // should handle failure better..
13454         this.transId = false;
13455         var a = response.argument;
13456         this.fireEvent("loadexception", this, a.node, response);
13457         if(typeof a.callback == "function"){
13458             a.callback(this, a.node);
13459         }
13460     }
13461 });/*
13462  * Based on:
13463  * Ext JS Library 1.1.1
13464  * Copyright(c) 2006-2007, Ext JS, LLC.
13465  *
13466  * Originally Released Under LGPL - original licence link has changed is not relivant.
13467  *
13468  * Fork - LGPL
13469  * <script type="text/javascript">
13470  */
13471
13472 /**
13473 * @class Roo.tree.TreeFilter
13474 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13475 * @param {TreePanel} tree
13476 * @param {Object} config (optional)
13477  */
13478 Roo.tree.TreeFilter = function(tree, config){
13479     this.tree = tree;
13480     this.filtered = {};
13481     Roo.apply(this, config);
13482 };
13483
13484 Roo.tree.TreeFilter.prototype = {
13485     clearBlank:false,
13486     reverse:false,
13487     autoClear:false,
13488     remove:false,
13489
13490      /**
13491      * Filter the data by a specific attribute.
13492      * @param {String/RegExp} value Either string that the attribute value
13493      * should start with or a RegExp to test against the attribute
13494      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13495      * @param {TreeNode} startNode (optional) The node to start the filter at.
13496      */
13497     filter : function(value, attr, startNode){
13498         attr = attr || "text";
13499         var f;
13500         if(typeof value == "string"){
13501             var vlen = value.length;
13502             // auto clear empty filter
13503             if(vlen == 0 && this.clearBlank){
13504                 this.clear();
13505                 return;
13506             }
13507             value = value.toLowerCase();
13508             f = function(n){
13509                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13510             };
13511         }else if(value.exec){ // regex?
13512             f = function(n){
13513                 return value.test(n.attributes[attr]);
13514             };
13515         }else{
13516             throw 'Illegal filter type, must be string or regex';
13517         }
13518         this.filterBy(f, null, startNode);
13519         },
13520
13521     /**
13522      * Filter by a function. The passed function will be called with each
13523      * node in the tree (or from the startNode). If the function returns true, the node is kept
13524      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13525      * @param {Function} fn The filter function
13526      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13527      */
13528     filterBy : function(fn, scope, startNode){
13529         startNode = startNode || this.tree.root;
13530         if(this.autoClear){
13531             this.clear();
13532         }
13533         var af = this.filtered, rv = this.reverse;
13534         var f = function(n){
13535             if(n == startNode){
13536                 return true;
13537             }
13538             if(af[n.id]){
13539                 return false;
13540             }
13541             var m = fn.call(scope || n, n);
13542             if(!m || rv){
13543                 af[n.id] = n;
13544                 n.ui.hide();
13545                 return false;
13546             }
13547             return true;
13548         };
13549         startNode.cascade(f);
13550         if(this.remove){
13551            for(var id in af){
13552                if(typeof id != "function"){
13553                    var n = af[id];
13554                    if(n && n.parentNode){
13555                        n.parentNode.removeChild(n);
13556                    }
13557                }
13558            }
13559         }
13560     },
13561
13562     /**
13563      * Clears the current filter. Note: with the "remove" option
13564      * set a filter cannot be cleared.
13565      */
13566     clear : function(){
13567         var t = this.tree;
13568         var af = this.filtered;
13569         for(var id in af){
13570             if(typeof id != "function"){
13571                 var n = af[id];
13572                 if(n){
13573                     n.ui.show();
13574                 }
13575             }
13576         }
13577         this.filtered = {};
13578     }
13579 };
13580 /*
13581  * Based on:
13582  * Ext JS Library 1.1.1
13583  * Copyright(c) 2006-2007, Ext JS, LLC.
13584  *
13585  * Originally Released Under LGPL - original licence link has changed is not relivant.
13586  *
13587  * Fork - LGPL
13588  * <script type="text/javascript">
13589  */
13590  
13591
13592 /**
13593  * @class Roo.tree.TreeSorter
13594  * Provides sorting of nodes in a TreePanel
13595  * 
13596  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13597  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13598  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13599  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13600  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13601  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13602  * @constructor
13603  * @param {TreePanel} tree
13604  * @param {Object} config
13605  */
13606 Roo.tree.TreeSorter = function(tree, config){
13607     Roo.apply(this, config);
13608     tree.on("beforechildrenrendered", this.doSort, this);
13609     tree.on("append", this.updateSort, this);
13610     tree.on("insert", this.updateSort, this);
13611     
13612     var dsc = this.dir && this.dir.toLowerCase() == "desc";
13613     var p = this.property || "text";
13614     var sortType = this.sortType;
13615     var fs = this.folderSort;
13616     var cs = this.caseSensitive === true;
13617     var leafAttr = this.leafAttr || 'leaf';
13618
13619     this.sortFn = function(n1, n2){
13620         if(fs){
13621             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13622                 return 1;
13623             }
13624             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13625                 return -1;
13626             }
13627         }
13628         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13629         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13630         if(v1 < v2){
13631                         return dsc ? +1 : -1;
13632                 }else if(v1 > v2){
13633                         return dsc ? -1 : +1;
13634         }else{
13635                 return 0;
13636         }
13637     };
13638 };
13639
13640 Roo.tree.TreeSorter.prototype = {
13641     doSort : function(node){
13642         node.sort(this.sortFn);
13643     },
13644     
13645     compareNodes : function(n1, n2){
13646         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
13647     },
13648     
13649     updateSort : function(tree, node){
13650         if(node.childrenRendered){
13651             this.doSort.defer(1, this, [node]);
13652         }
13653     }
13654 };/*
13655  * Based on:
13656  * Ext JS Library 1.1.1
13657  * Copyright(c) 2006-2007, Ext JS, LLC.
13658  *
13659  * Originally Released Under LGPL - original licence link has changed is not relivant.
13660  *
13661  * Fork - LGPL
13662  * <script type="text/javascript">
13663  */
13664
13665 if(Roo.dd.DropZone){
13666     
13667 Roo.tree.TreeDropZone = function(tree, config){
13668     this.allowParentInsert = false;
13669     this.allowContainerDrop = false;
13670     this.appendOnly = false;
13671     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
13672     this.tree = tree;
13673     this.lastInsertClass = "x-tree-no-status";
13674     this.dragOverData = {};
13675 };
13676
13677 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
13678     ddGroup : "TreeDD",
13679     scroll:  true,
13680     
13681     expandDelay : 1000,
13682     
13683     expandNode : function(node){
13684         if(node.hasChildNodes() && !node.isExpanded()){
13685             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
13686         }
13687     },
13688     
13689     queueExpand : function(node){
13690         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
13691     },
13692     
13693     cancelExpand : function(){
13694         if(this.expandProcId){
13695             clearTimeout(this.expandProcId);
13696             this.expandProcId = false;
13697         }
13698     },
13699     
13700     isValidDropPoint : function(n, pt, dd, e, data){
13701         if(!n || !data){ return false; }
13702         var targetNode = n.node;
13703         var dropNode = data.node;
13704         // default drop rules
13705         if(!(targetNode && targetNode.isTarget && pt)){
13706             return false;
13707         }
13708         if(pt == "append" && targetNode.allowChildren === false){
13709             return false;
13710         }
13711         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
13712             return false;
13713         }
13714         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
13715             return false;
13716         }
13717         // reuse the object
13718         var overEvent = this.dragOverData;
13719         overEvent.tree = this.tree;
13720         overEvent.target = targetNode;
13721         overEvent.data = data;
13722         overEvent.point = pt;
13723         overEvent.source = dd;
13724         overEvent.rawEvent = e;
13725         overEvent.dropNode = dropNode;
13726         overEvent.cancel = false;  
13727         var result = this.tree.fireEvent("nodedragover", overEvent);
13728         return overEvent.cancel === false && result !== false;
13729     },
13730     
13731     getDropPoint : function(e, n, dd)
13732     {
13733         var tn = n.node;
13734         if(tn.isRoot){
13735             return tn.allowChildren !== false ? "append" : false; // always append for root
13736         }
13737         var dragEl = n.ddel;
13738         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
13739         var y = Roo.lib.Event.getPageY(e);
13740         //var noAppend = tn.allowChildren === false || tn.isLeaf();
13741         
13742         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
13743         var noAppend = tn.allowChildren === false;
13744         if(this.appendOnly || tn.parentNode.allowChildren === false){
13745             return noAppend ? false : "append";
13746         }
13747         var noBelow = false;
13748         if(!this.allowParentInsert){
13749             noBelow = tn.hasChildNodes() && tn.isExpanded();
13750         }
13751         var q = (b - t) / (noAppend ? 2 : 3);
13752         if(y >= t && y < (t + q)){
13753             return "above";
13754         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
13755             return "below";
13756         }else{
13757             return "append";
13758         }
13759     },
13760     
13761     onNodeEnter : function(n, dd, e, data)
13762     {
13763         this.cancelExpand();
13764     },
13765     
13766     onNodeOver : function(n, dd, e, data)
13767     {
13768        
13769         var pt = this.getDropPoint(e, n, dd);
13770         var node = n.node;
13771         
13772         // auto node expand check
13773         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
13774             this.queueExpand(node);
13775         }else if(pt != "append"){
13776             this.cancelExpand();
13777         }
13778         
13779         // set the insert point style on the target node
13780         var returnCls = this.dropNotAllowed;
13781         if(this.isValidDropPoint(n, pt, dd, e, data)){
13782            if(pt){
13783                var el = n.ddel;
13784                var cls;
13785                if(pt == "above"){
13786                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
13787                    cls = "x-tree-drag-insert-above";
13788                }else if(pt == "below"){
13789                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
13790                    cls = "x-tree-drag-insert-below";
13791                }else{
13792                    returnCls = "x-tree-drop-ok-append";
13793                    cls = "x-tree-drag-append";
13794                }
13795                if(this.lastInsertClass != cls){
13796                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
13797                    this.lastInsertClass = cls;
13798                }
13799            }
13800        }
13801        return returnCls;
13802     },
13803     
13804     onNodeOut : function(n, dd, e, data){
13805         
13806         this.cancelExpand();
13807         this.removeDropIndicators(n);
13808     },
13809     
13810     onNodeDrop : function(n, dd, e, data){
13811         var point = this.getDropPoint(e, n, dd);
13812         var targetNode = n.node;
13813         targetNode.ui.startDrop();
13814         if(!this.isValidDropPoint(n, point, dd, e, data)){
13815             targetNode.ui.endDrop();
13816             return false;
13817         }
13818         // first try to find the drop node
13819         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
13820         var dropEvent = {
13821             tree : this.tree,
13822             target: targetNode,
13823             data: data,
13824             point: point,
13825             source: dd,
13826             rawEvent: e,
13827             dropNode: dropNode,
13828             cancel: !dropNode   
13829         };
13830         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
13831         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
13832             targetNode.ui.endDrop();
13833             return false;
13834         }
13835         // allow target changing
13836         targetNode = dropEvent.target;
13837         if(point == "append" && !targetNode.isExpanded()){
13838             targetNode.expand(false, null, function(){
13839                 this.completeDrop(dropEvent);
13840             }.createDelegate(this));
13841         }else{
13842             this.completeDrop(dropEvent);
13843         }
13844         return true;
13845     },
13846     
13847     completeDrop : function(de){
13848         var ns = de.dropNode, p = de.point, t = de.target;
13849         if(!(ns instanceof Array)){
13850             ns = [ns];
13851         }
13852         var n;
13853         for(var i = 0, len = ns.length; i < len; i++){
13854             n = ns[i];
13855             if(p == "above"){
13856                 t.parentNode.insertBefore(n, t);
13857             }else if(p == "below"){
13858                 t.parentNode.insertBefore(n, t.nextSibling);
13859             }else{
13860                 t.appendChild(n);
13861             }
13862         }
13863         n.ui.focus();
13864         if(this.tree.hlDrop){
13865             n.ui.highlight();
13866         }
13867         t.ui.endDrop();
13868         this.tree.fireEvent("nodedrop", de);
13869     },
13870     
13871     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
13872         if(this.tree.hlDrop){
13873             dropNode.ui.focus();
13874             dropNode.ui.highlight();
13875         }
13876         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
13877     },
13878     
13879     getTree : function(){
13880         return this.tree;
13881     },
13882     
13883     removeDropIndicators : function(n){
13884         if(n && n.ddel){
13885             var el = n.ddel;
13886             Roo.fly(el).removeClass([
13887                     "x-tree-drag-insert-above",
13888                     "x-tree-drag-insert-below",
13889                     "x-tree-drag-append"]);
13890             this.lastInsertClass = "_noclass";
13891         }
13892     },
13893     
13894     beforeDragDrop : function(target, e, id){
13895         this.cancelExpand();
13896         return true;
13897     },
13898     
13899     afterRepair : function(data){
13900         if(data && Roo.enableFx){
13901             data.node.ui.highlight();
13902         }
13903         this.hideProxy();
13904     } 
13905     
13906 });
13907
13908 }
13909 /*
13910  * Based on:
13911  * Ext JS Library 1.1.1
13912  * Copyright(c) 2006-2007, Ext JS, LLC.
13913  *
13914  * Originally Released Under LGPL - original licence link has changed is not relivant.
13915  *
13916  * Fork - LGPL
13917  * <script type="text/javascript">
13918  */
13919  
13920
13921 if(Roo.dd.DragZone){
13922 Roo.tree.TreeDragZone = function(tree, config){
13923     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
13924     this.tree = tree;
13925 };
13926
13927 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
13928     ddGroup : "TreeDD",
13929    
13930     onBeforeDrag : function(data, e){
13931         var n = data.node;
13932         return n && n.draggable && !n.disabled;
13933     },
13934      
13935     
13936     onInitDrag : function(e){
13937         var data = this.dragData;
13938         this.tree.getSelectionModel().select(data.node);
13939         this.proxy.update("");
13940         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
13941         this.tree.fireEvent("startdrag", this.tree, data.node, e);
13942     },
13943     
13944     getRepairXY : function(e, data){
13945         return data.node.ui.getDDRepairXY();
13946     },
13947     
13948     onEndDrag : function(data, e){
13949         this.tree.fireEvent("enddrag", this.tree, data.node, e);
13950         
13951         
13952     },
13953     
13954     onValidDrop : function(dd, e, id){
13955         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
13956         this.hideProxy();
13957     },
13958     
13959     beforeInvalidDrop : function(e, id){
13960         // this scrolls the original position back into view
13961         var sm = this.tree.getSelectionModel();
13962         sm.clearSelections();
13963         sm.select(this.dragData.node);
13964     }
13965 });
13966 }/*
13967  * Based on:
13968  * Ext JS Library 1.1.1
13969  * Copyright(c) 2006-2007, Ext JS, LLC.
13970  *
13971  * Originally Released Under LGPL - original licence link has changed is not relivant.
13972  *
13973  * Fork - LGPL
13974  * <script type="text/javascript">
13975  */
13976 /**
13977  * @class Roo.tree.TreeEditor
13978  * @extends Roo.Editor
13979  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
13980  * as the editor field.
13981  * @constructor
13982  * @param {Object} config (used to be the tree panel.)
13983  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
13984  * 
13985  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
13986  * @cfg {Roo.form.TextField} field [required] The field configuration
13987  *
13988  * 
13989  */
13990 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
13991     var tree = config;
13992     var field;
13993     if (oldconfig) { // old style..
13994         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
13995     } else {
13996         // new style..
13997         tree = config.tree;
13998         config.field = config.field  || {};
13999         config.field.xtype = 'TextField';
14000         field = Roo.factory(config.field, Roo.form);
14001     }
14002     config = config || {};
14003     
14004     
14005     this.addEvents({
14006         /**
14007          * @event beforenodeedit
14008          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14009          * false from the handler of this event.
14010          * @param {Editor} this
14011          * @param {Roo.tree.Node} node 
14012          */
14013         "beforenodeedit" : true
14014     });
14015     
14016     //Roo.log(config);
14017     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14018
14019     this.tree = tree;
14020
14021     tree.on('beforeclick', this.beforeNodeClick, this);
14022     tree.getTreeEl().on('mousedown', this.hide, this);
14023     this.on('complete', this.updateNode, this);
14024     this.on('beforestartedit', this.fitToTree, this);
14025     this.on('startedit', this.bindScroll, this, {delay:10});
14026     this.on('specialkey', this.onSpecialKey, this);
14027 };
14028
14029 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14030     /**
14031      * @cfg {String} alignment
14032      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14033      */
14034     alignment: "l-l",
14035     // inherit
14036     autoSize: false,
14037     /**
14038      * @cfg {Boolean} hideEl
14039      * True to hide the bound element while the editor is displayed (defaults to false)
14040      */
14041     hideEl : false,
14042     /**
14043      * @cfg {String} cls
14044      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14045      */
14046     cls: "x-small-editor x-tree-editor",
14047     /**
14048      * @cfg {Boolean} shim
14049      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14050      */
14051     shim:false,
14052     // inherit
14053     shadow:"frame",
14054     /**
14055      * @cfg {Number} maxWidth
14056      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14057      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14058      * scroll and client offsets into account prior to each edit.
14059      */
14060     maxWidth: 250,
14061
14062     editDelay : 350,
14063
14064     // private
14065     fitToTree : function(ed, el){
14066         var td = this.tree.getTreeEl().dom, nd = el.dom;
14067         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14068             td.scrollLeft = nd.offsetLeft;
14069         }
14070         var w = Math.min(
14071                 this.maxWidth,
14072                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14073         this.setSize(w, '');
14074         
14075         return this.fireEvent('beforenodeedit', this, this.editNode);
14076         
14077     },
14078
14079     // private
14080     triggerEdit : function(node){
14081         this.completeEdit();
14082         this.editNode = node;
14083         this.startEdit(node.ui.textNode, node.text);
14084     },
14085
14086     // private
14087     bindScroll : function(){
14088         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14089     },
14090
14091     // private
14092     beforeNodeClick : function(node, e){
14093         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14094         this.lastClick = new Date();
14095         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14096             e.stopEvent();
14097             this.triggerEdit(node);
14098             return false;
14099         }
14100         return true;
14101     },
14102
14103     // private
14104     updateNode : function(ed, value){
14105         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14106         this.editNode.setText(value);
14107     },
14108
14109     // private
14110     onHide : function(){
14111         Roo.tree.TreeEditor.superclass.onHide.call(this);
14112         if(this.editNode){
14113             this.editNode.ui.focus();
14114         }
14115     },
14116
14117     // private
14118     onSpecialKey : function(field, e){
14119         var k = e.getKey();
14120         if(k == e.ESC){
14121             e.stopEvent();
14122             this.cancelEdit();
14123         }else if(k == e.ENTER && !e.hasModifier()){
14124             e.stopEvent();
14125             this.completeEdit();
14126         }
14127     }
14128 });//<Script type="text/javascript">
14129 /*
14130  * Based on:
14131  * Ext JS Library 1.1.1
14132  * Copyright(c) 2006-2007, Ext JS, LLC.
14133  *
14134  * Originally Released Under LGPL - original licence link has changed is not relivant.
14135  *
14136  * Fork - LGPL
14137  * <script type="text/javascript">
14138  */
14139  
14140 /**
14141  * Not documented??? - probably should be...
14142  */
14143
14144 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14145     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14146     
14147     renderElements : function(n, a, targetNode, bulkRender){
14148         //consel.log("renderElements?");
14149         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14150
14151         var t = n.getOwnerTree();
14152         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14153         
14154         var cols = t.columns;
14155         var bw = t.borderWidth;
14156         var c = cols[0];
14157         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14158          var cb = typeof a.checked == "boolean";
14159         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14160         var colcls = 'x-t-' + tid + '-c0';
14161         var buf = [
14162             '<li class="x-tree-node">',
14163             
14164                 
14165                 '<div class="x-tree-node-el ', a.cls,'">',
14166                     // extran...
14167                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14168                 
14169                 
14170                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14171                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14172                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14173                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14174                            (a.iconCls ? ' '+a.iconCls : ''),
14175                            '" unselectable="on" />',
14176                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14177                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14178                              
14179                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14180                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14181                             '<span unselectable="on" qtip="' + tx + '">',
14182                              tx,
14183                              '</span></a>' ,
14184                     '</div>',
14185                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14186                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14187                  ];
14188         for(var i = 1, len = cols.length; i < len; i++){
14189             c = cols[i];
14190             colcls = 'x-t-' + tid + '-c' +i;
14191             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14192             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14193                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14194                       "</div>");
14195          }
14196          
14197          buf.push(
14198             '</a>',
14199             '<div class="x-clear"></div></div>',
14200             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14201             "</li>");
14202         
14203         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14204             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14205                                 n.nextSibling.ui.getEl(), buf.join(""));
14206         }else{
14207             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14208         }
14209         var el = this.wrap.firstChild;
14210         this.elRow = el;
14211         this.elNode = el.firstChild;
14212         this.ranchor = el.childNodes[1];
14213         this.ctNode = this.wrap.childNodes[1];
14214         var cs = el.firstChild.childNodes;
14215         this.indentNode = cs[0];
14216         this.ecNode = cs[1];
14217         this.iconNode = cs[2];
14218         var index = 3;
14219         if(cb){
14220             this.checkbox = cs[3];
14221             index++;
14222         }
14223         this.anchor = cs[index];
14224         
14225         this.textNode = cs[index].firstChild;
14226         
14227         //el.on("click", this.onClick, this);
14228         //el.on("dblclick", this.onDblClick, this);
14229         
14230         
14231        // console.log(this);
14232     },
14233     initEvents : function(){
14234         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14235         
14236             
14237         var a = this.ranchor;
14238
14239         var el = Roo.get(a);
14240
14241         if(Roo.isOpera){ // opera render bug ignores the CSS
14242             el.setStyle("text-decoration", "none");
14243         }
14244
14245         el.on("click", this.onClick, this);
14246         el.on("dblclick", this.onDblClick, this);
14247         el.on("contextmenu", this.onContextMenu, this);
14248         
14249     },
14250     
14251     /*onSelectedChange : function(state){
14252         if(state){
14253             this.focus();
14254             this.addClass("x-tree-selected");
14255         }else{
14256             //this.blur();
14257             this.removeClass("x-tree-selected");
14258         }
14259     },*/
14260     addClass : function(cls){
14261         if(this.elRow){
14262             Roo.fly(this.elRow).addClass(cls);
14263         }
14264         
14265     },
14266     
14267     
14268     removeClass : function(cls){
14269         if(this.elRow){
14270             Roo.fly(this.elRow).removeClass(cls);
14271         }
14272     }
14273
14274     
14275     
14276 });//<Script type="text/javascript">
14277
14278 /*
14279  * Based on:
14280  * Ext JS Library 1.1.1
14281  * Copyright(c) 2006-2007, Ext JS, LLC.
14282  *
14283  * Originally Released Under LGPL - original licence link has changed is not relivant.
14284  *
14285  * Fork - LGPL
14286  * <script type="text/javascript">
14287  */
14288  
14289
14290 /**
14291  * @class Roo.tree.ColumnTree
14292  * @extends Roo.tree.TreePanel
14293  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14294  * @cfg {int} borderWidth  compined right/left border allowance
14295  * @constructor
14296  * @param {String/HTMLElement/Element} el The container element
14297  * @param {Object} config
14298  */
14299 Roo.tree.ColumnTree =  function(el, config)
14300 {
14301    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14302    this.addEvents({
14303         /**
14304         * @event resize
14305         * Fire this event on a container when it resizes
14306         * @param {int} w Width
14307         * @param {int} h Height
14308         */
14309        "resize" : true
14310     });
14311     this.on('resize', this.onResize, this);
14312 };
14313
14314 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14315     //lines:false,
14316     
14317     
14318     borderWidth: Roo.isBorderBox ? 0 : 2, 
14319     headEls : false,
14320     
14321     render : function(){
14322         // add the header.....
14323        
14324         Roo.tree.ColumnTree.superclass.render.apply(this);
14325         
14326         this.el.addClass('x-column-tree');
14327         
14328         this.headers = this.el.createChild(
14329             {cls:'x-tree-headers'},this.innerCt.dom);
14330    
14331         var cols = this.columns, c;
14332         var totalWidth = 0;
14333         this.headEls = [];
14334         var  len = cols.length;
14335         for(var i = 0; i < len; i++){
14336              c = cols[i];
14337              totalWidth += c.width;
14338             this.headEls.push(this.headers.createChild({
14339                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14340                  cn: {
14341                      cls:'x-tree-hd-text',
14342                      html: c.header
14343                  },
14344                  style:'width:'+(c.width-this.borderWidth)+'px;'
14345              }));
14346         }
14347         this.headers.createChild({cls:'x-clear'});
14348         // prevent floats from wrapping when clipped
14349         this.headers.setWidth(totalWidth);
14350         //this.innerCt.setWidth(totalWidth);
14351         this.innerCt.setStyle({ overflow: 'auto' });
14352         this.onResize(this.width, this.height);
14353              
14354         
14355     },
14356     onResize : function(w,h)
14357     {
14358         this.height = h;
14359         this.width = w;
14360         // resize cols..
14361         this.innerCt.setWidth(this.width);
14362         this.innerCt.setHeight(this.height-20);
14363         
14364         // headers...
14365         var cols = this.columns, c;
14366         var totalWidth = 0;
14367         var expEl = false;
14368         var len = cols.length;
14369         for(var i = 0; i < len; i++){
14370             c = cols[i];
14371             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14372                 // it's the expander..
14373                 expEl  = this.headEls[i];
14374                 continue;
14375             }
14376             totalWidth += c.width;
14377             
14378         }
14379         if (expEl) {
14380             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14381         }
14382         this.headers.setWidth(w-20);
14383
14384         
14385         
14386         
14387     }
14388 });
14389 /*
14390  * Based on:
14391  * Ext JS Library 1.1.1
14392  * Copyright(c) 2006-2007, Ext JS, LLC.
14393  *
14394  * Originally Released Under LGPL - original licence link has changed is not relivant.
14395  *
14396  * Fork - LGPL
14397  * <script type="text/javascript">
14398  */
14399  
14400 /**
14401  * @class Roo.menu.Menu
14402  * @extends Roo.util.Observable
14403  * @children Roo.menu.BaseItem
14404  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14405  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14406  * @constructor
14407  * Creates a new Menu
14408  * @param {Object} config Configuration options
14409  */
14410 Roo.menu.Menu = function(config){
14411     
14412     Roo.menu.Menu.superclass.constructor.call(this, config);
14413     
14414     this.id = this.id || Roo.id();
14415     this.addEvents({
14416         /**
14417          * @event beforeshow
14418          * Fires before this menu is displayed
14419          * @param {Roo.menu.Menu} this
14420          */
14421         beforeshow : true,
14422         /**
14423          * @event beforehide
14424          * Fires before this menu is hidden
14425          * @param {Roo.menu.Menu} this
14426          */
14427         beforehide : true,
14428         /**
14429          * @event show
14430          * Fires after this menu is displayed
14431          * @param {Roo.menu.Menu} this
14432          */
14433         show : true,
14434         /**
14435          * @event hide
14436          * Fires after this menu is hidden
14437          * @param {Roo.menu.Menu} this
14438          */
14439         hide : true,
14440         /**
14441          * @event click
14442          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14443          * @param {Roo.menu.Menu} this
14444          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14445          * @param {Roo.EventObject} e
14446          */
14447         click : true,
14448         /**
14449          * @event mouseover
14450          * Fires when the mouse is hovering over this menu
14451          * @param {Roo.menu.Menu} this
14452          * @param {Roo.EventObject} e
14453          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14454          */
14455         mouseover : true,
14456         /**
14457          * @event mouseout
14458          * Fires when the mouse exits this menu
14459          * @param {Roo.menu.Menu} this
14460          * @param {Roo.EventObject} e
14461          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14462          */
14463         mouseout : true,
14464         /**
14465          * @event itemclick
14466          * Fires when a menu item contained in this menu is clicked
14467          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14468          * @param {Roo.EventObject} e
14469          */
14470         itemclick: true
14471     });
14472     if (this.registerMenu) {
14473         Roo.menu.MenuMgr.register(this);
14474     }
14475     
14476     var mis = this.items;
14477     this.items = new Roo.util.MixedCollection();
14478     if(mis){
14479         this.add.apply(this, mis);
14480     }
14481 };
14482
14483 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14484     /**
14485      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14486      */
14487     minWidth : 120,
14488     /**
14489      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14490      * for bottom-right shadow (defaults to "sides")
14491      */
14492     shadow : "sides",
14493     /**
14494      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14495      * this menu (defaults to "tl-tr?")
14496      */
14497     subMenuAlign : "tl-tr?",
14498     /**
14499      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14500      * relative to its element of origin (defaults to "tl-bl?")
14501      */
14502     defaultAlign : "tl-bl?",
14503     /**
14504      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14505      */
14506     allowOtherMenus : false,
14507     /**
14508      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14509      */
14510     registerMenu : true,
14511
14512     hidden:true,
14513
14514     // private
14515     render : function(){
14516         if(this.el){
14517             return;
14518         }
14519         var el = this.el = new Roo.Layer({
14520             cls: "x-menu",
14521             shadow:this.shadow,
14522             constrain: false,
14523             parentEl: this.parentEl || document.body,
14524             zindex:15000
14525         });
14526
14527         this.keyNav = new Roo.menu.MenuNav(this);
14528
14529         if(this.plain){
14530             el.addClass("x-menu-plain");
14531         }
14532         if(this.cls){
14533             el.addClass(this.cls);
14534         }
14535         // generic focus element
14536         this.focusEl = el.createChild({
14537             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14538         });
14539         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14540         //disabling touch- as it's causing issues ..
14541         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14542         ul.on('click'   , this.onClick, this);
14543         
14544         
14545         ul.on("mouseover", this.onMouseOver, this);
14546         ul.on("mouseout", this.onMouseOut, this);
14547         this.items.each(function(item){
14548             if (item.hidden) {
14549                 return;
14550             }
14551             
14552             var li = document.createElement("li");
14553             li.className = "x-menu-list-item";
14554             ul.dom.appendChild(li);
14555             item.render(li, this);
14556         }, this);
14557         this.ul = ul;
14558         this.autoWidth();
14559     },
14560
14561     // private
14562     autoWidth : function(){
14563         var el = this.el, ul = this.ul;
14564         if(!el){
14565             return;
14566         }
14567         var w = this.width;
14568         if(w){
14569             el.setWidth(w);
14570         }else if(Roo.isIE){
14571             el.setWidth(this.minWidth);
14572             var t = el.dom.offsetWidth; // force recalc
14573             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14574         }
14575     },
14576
14577     // private
14578     delayAutoWidth : function(){
14579         if(this.rendered){
14580             if(!this.awTask){
14581                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14582             }
14583             this.awTask.delay(20);
14584         }
14585     },
14586
14587     // private
14588     findTargetItem : function(e){
14589         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14590         if(t && t.menuItemId){
14591             return this.items.get(t.menuItemId);
14592         }
14593     },
14594
14595     // private
14596     onClick : function(e){
14597         Roo.log("menu.onClick");
14598         var t = this.findTargetItem(e);
14599         if(!t){
14600             return;
14601         }
14602         Roo.log(e);
14603         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14604             if(t == this.activeItem && t.shouldDeactivate(e)){
14605                 this.activeItem.deactivate();
14606                 delete this.activeItem;
14607                 return;
14608             }
14609             if(t.canActivate){
14610                 this.setActiveItem(t, true);
14611             }
14612             return;
14613             
14614             
14615         }
14616         
14617         t.onClick(e);
14618         this.fireEvent("click", this, t, e);
14619     },
14620
14621     // private
14622     setActiveItem : function(item, autoExpand){
14623         if(item != this.activeItem){
14624             if(this.activeItem){
14625                 this.activeItem.deactivate();
14626             }
14627             this.activeItem = item;
14628             item.activate(autoExpand);
14629         }else if(autoExpand){
14630             item.expandMenu();
14631         }
14632     },
14633
14634     // private
14635     tryActivate : function(start, step){
14636         var items = this.items;
14637         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
14638             var item = items.get(i);
14639             if(!item.disabled && item.canActivate){
14640                 this.setActiveItem(item, false);
14641                 return item;
14642             }
14643         }
14644         return false;
14645     },
14646
14647     // private
14648     onMouseOver : function(e){
14649         var t;
14650         if(t = this.findTargetItem(e)){
14651             if(t.canActivate && !t.disabled){
14652                 this.setActiveItem(t, true);
14653             }
14654         }
14655         this.fireEvent("mouseover", this, e, t);
14656     },
14657
14658     // private
14659     onMouseOut : function(e){
14660         var t;
14661         if(t = this.findTargetItem(e)){
14662             if(t == this.activeItem && t.shouldDeactivate(e)){
14663                 this.activeItem.deactivate();
14664                 delete this.activeItem;
14665             }
14666         }
14667         this.fireEvent("mouseout", this, e, t);
14668     },
14669
14670     /**
14671      * Read-only.  Returns true if the menu is currently displayed, else false.
14672      * @type Boolean
14673      */
14674     isVisible : function(){
14675         return this.el && !this.hidden;
14676     },
14677
14678     /**
14679      * Displays this menu relative to another element
14680      * @param {String/HTMLElement/Roo.Element} element The element to align to
14681      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
14682      * the element (defaults to this.defaultAlign)
14683      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14684      */
14685     show : function(el, pos, parentMenu){
14686         this.parentMenu = parentMenu;
14687         if(!this.el){
14688             this.render();
14689         }
14690         this.fireEvent("beforeshow", this);
14691         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
14692     },
14693
14694     /**
14695      * Displays this menu at a specific xy position
14696      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
14697      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14698      */
14699     showAt : function(xy, parentMenu, /* private: */_e){
14700         this.parentMenu = parentMenu;
14701         if(!this.el){
14702             this.render();
14703         }
14704         if(_e !== false){
14705             this.fireEvent("beforeshow", this);
14706             xy = this.el.adjustForConstraints(xy);
14707         }
14708         this.el.setXY(xy);
14709         this.el.show();
14710         this.hidden = false;
14711         this.focus();
14712         this.fireEvent("show", this);
14713     },
14714
14715     focus : function(){
14716         if(!this.hidden){
14717             this.doFocus.defer(50, this);
14718         }
14719     },
14720
14721     doFocus : function(){
14722         if(!this.hidden){
14723             this.focusEl.focus();
14724         }
14725     },
14726
14727     /**
14728      * Hides this menu and optionally all parent menus
14729      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
14730      */
14731     hide : function(deep){
14732         if(this.el && this.isVisible()){
14733             this.fireEvent("beforehide", this);
14734             if(this.activeItem){
14735                 this.activeItem.deactivate();
14736                 this.activeItem = null;
14737             }
14738             this.el.hide();
14739             this.hidden = true;
14740             this.fireEvent("hide", this);
14741         }
14742         if(deep === true && this.parentMenu){
14743             this.parentMenu.hide(true);
14744         }
14745     },
14746
14747     /**
14748      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
14749      * Any of the following are valid:
14750      * <ul>
14751      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
14752      * <li>An HTMLElement object which will be converted to a menu item</li>
14753      * <li>A menu item config object that will be created as a new menu item</li>
14754      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
14755      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
14756      * </ul>
14757      * Usage:
14758      * <pre><code>
14759 // Create the menu
14760 var menu = new Roo.menu.Menu();
14761
14762 // Create a menu item to add by reference
14763 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
14764
14765 // Add a bunch of items at once using different methods.
14766 // Only the last item added will be returned.
14767 var item = menu.add(
14768     menuItem,                // add existing item by ref
14769     'Dynamic Item',          // new TextItem
14770     '-',                     // new separator
14771     { text: 'Config Item' }  // new item by config
14772 );
14773 </code></pre>
14774      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
14775      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
14776      */
14777     add : function(){
14778         var a = arguments, l = a.length, item;
14779         for(var i = 0; i < l; i++){
14780             var el = a[i];
14781             if ((typeof(el) == "object") && el.xtype && el.xns) {
14782                 el = Roo.factory(el, Roo.menu);
14783             }
14784             
14785             if(el.render){ // some kind of Item
14786                 item = this.addItem(el);
14787             }else if(typeof el == "string"){ // string
14788                 if(el == "separator" || el == "-"){
14789                     item = this.addSeparator();
14790                 }else{
14791                     item = this.addText(el);
14792                 }
14793             }else if(el.tagName || el.el){ // element
14794                 item = this.addElement(el);
14795             }else if(typeof el == "object"){ // must be menu item config?
14796                 item = this.addMenuItem(el);
14797             }
14798         }
14799         return item;
14800     },
14801
14802     /**
14803      * Returns this menu's underlying {@link Roo.Element} object
14804      * @return {Roo.Element} The element
14805      */
14806     getEl : function(){
14807         if(!this.el){
14808             this.render();
14809         }
14810         return this.el;
14811     },
14812
14813     /**
14814      * Adds a separator bar to the menu
14815      * @return {Roo.menu.Item} The menu item that was added
14816      */
14817     addSeparator : function(){
14818         return this.addItem(new Roo.menu.Separator());
14819     },
14820
14821     /**
14822      * Adds an {@link Roo.Element} object to the menu
14823      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
14824      * @return {Roo.menu.Item} The menu item that was added
14825      */
14826     addElement : function(el){
14827         return this.addItem(new Roo.menu.BaseItem(el));
14828     },
14829
14830     /**
14831      * Adds an existing object based on {@link Roo.menu.Item} to the menu
14832      * @param {Roo.menu.Item} item The menu item to add
14833      * @return {Roo.menu.Item} The menu item that was added
14834      */
14835     addItem : function(item){
14836         this.items.add(item);
14837         if(this.ul){
14838             var li = document.createElement("li");
14839             li.className = "x-menu-list-item";
14840             this.ul.dom.appendChild(li);
14841             item.render(li, this);
14842             this.delayAutoWidth();
14843         }
14844         return item;
14845     },
14846
14847     /**
14848      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
14849      * @param {Object} config A MenuItem config object
14850      * @return {Roo.menu.Item} The menu item that was added
14851      */
14852     addMenuItem : function(config){
14853         if(!(config instanceof Roo.menu.Item)){
14854             if(typeof config.checked == "boolean"){ // must be check menu item config?
14855                 config = new Roo.menu.CheckItem(config);
14856             }else{
14857                 config = new Roo.menu.Item(config);
14858             }
14859         }
14860         return this.addItem(config);
14861     },
14862
14863     /**
14864      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
14865      * @param {String} text The text to display in the menu item
14866      * @return {Roo.menu.Item} The menu item that was added
14867      */
14868     addText : function(text){
14869         return this.addItem(new Roo.menu.TextItem({ text : text }));
14870     },
14871
14872     /**
14873      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
14874      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
14875      * @param {Roo.menu.Item} item The menu item to add
14876      * @return {Roo.menu.Item} The menu item that was added
14877      */
14878     insert : function(index, item){
14879         this.items.insert(index, item);
14880         if(this.ul){
14881             var li = document.createElement("li");
14882             li.className = "x-menu-list-item";
14883             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
14884             item.render(li, this);
14885             this.delayAutoWidth();
14886         }
14887         return item;
14888     },
14889
14890     /**
14891      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
14892      * @param {Roo.menu.Item} item The menu item to remove
14893      */
14894     remove : function(item){
14895         this.items.removeKey(item.id);
14896         item.destroy();
14897     },
14898
14899     /**
14900      * Removes and destroys all items in the menu
14901      */
14902     removeAll : function(){
14903         var f;
14904         while(f = this.items.first()){
14905             this.remove(f);
14906         }
14907     }
14908 });
14909
14910 // MenuNav is a private utility class used internally by the Menu
14911 Roo.menu.MenuNav = function(menu){
14912     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
14913     this.scope = this.menu = menu;
14914 };
14915
14916 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
14917     doRelay : function(e, h){
14918         var k = e.getKey();
14919         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
14920             this.menu.tryActivate(0, 1);
14921             return false;
14922         }
14923         return h.call(this.scope || this, e, this.menu);
14924     },
14925
14926     up : function(e, m){
14927         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
14928             m.tryActivate(m.items.length-1, -1);
14929         }
14930     },
14931
14932     down : function(e, m){
14933         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
14934             m.tryActivate(0, 1);
14935         }
14936     },
14937
14938     right : function(e, m){
14939         if(m.activeItem){
14940             m.activeItem.expandMenu(true);
14941         }
14942     },
14943
14944     left : function(e, m){
14945         m.hide();
14946         if(m.parentMenu && m.parentMenu.activeItem){
14947             m.parentMenu.activeItem.activate();
14948         }
14949     },
14950
14951     enter : function(e, m){
14952         if(m.activeItem){
14953             e.stopPropagation();
14954             m.activeItem.onClick(e);
14955             m.fireEvent("click", this, m.activeItem);
14956             return true;
14957         }
14958     }
14959 });/*
14960  * Based on:
14961  * Ext JS Library 1.1.1
14962  * Copyright(c) 2006-2007, Ext JS, LLC.
14963  *
14964  * Originally Released Under LGPL - original licence link has changed is not relivant.
14965  *
14966  * Fork - LGPL
14967  * <script type="text/javascript">
14968  */
14969  
14970 /**
14971  * @class Roo.menu.MenuMgr
14972  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
14973  * @static
14974  */
14975 Roo.menu.MenuMgr = function(){
14976    var menus, active, groups = {}, attached = false, lastShow = new Date();
14977
14978    // private - called when first menu is created
14979    function init(){
14980        menus = {};
14981        active = new Roo.util.MixedCollection();
14982        Roo.get(document).addKeyListener(27, function(){
14983            if(active.length > 0){
14984                hideAll();
14985            }
14986        });
14987    }
14988
14989    // private
14990    function hideAll(){
14991        if(active && active.length > 0){
14992            var c = active.clone();
14993            c.each(function(m){
14994                m.hide();
14995            });
14996        }
14997    }
14998
14999    // private
15000    function onHide(m){
15001        active.remove(m);
15002        if(active.length < 1){
15003            Roo.get(document).un("mousedown", onMouseDown);
15004            attached = false;
15005        }
15006    }
15007
15008    // private
15009    function onShow(m){
15010        var last = active.last();
15011        lastShow = new Date();
15012        active.add(m);
15013        if(!attached){
15014            Roo.get(document).on("mousedown", onMouseDown);
15015            attached = true;
15016        }
15017        if(m.parentMenu){
15018           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15019           m.parentMenu.activeChild = m;
15020        }else if(last && last.isVisible()){
15021           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15022        }
15023    }
15024
15025    // private
15026    function onBeforeHide(m){
15027        if(m.activeChild){
15028            m.activeChild.hide();
15029        }
15030        if(m.autoHideTimer){
15031            clearTimeout(m.autoHideTimer);
15032            delete m.autoHideTimer;
15033        }
15034    }
15035
15036    // private
15037    function onBeforeShow(m){
15038        var pm = m.parentMenu;
15039        if(!pm && !m.allowOtherMenus){
15040            hideAll();
15041        }else if(pm && pm.activeChild && active != m){
15042            pm.activeChild.hide();
15043        }
15044    }
15045
15046    // private
15047    function onMouseDown(e){
15048        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15049            hideAll();
15050        }
15051    }
15052
15053    // private
15054    function onBeforeCheck(mi, state){
15055        if(state){
15056            var g = groups[mi.group];
15057            for(var i = 0, l = g.length; i < l; i++){
15058                if(g[i] != mi){
15059                    g[i].setChecked(false);
15060                }
15061            }
15062        }
15063    }
15064
15065    return {
15066
15067        /**
15068         * Hides all menus that are currently visible
15069         */
15070        hideAll : function(){
15071             hideAll();  
15072        },
15073
15074        // private
15075        register : function(menu){
15076            if(!menus){
15077                init();
15078            }
15079            menus[menu.id] = menu;
15080            menu.on("beforehide", onBeforeHide);
15081            menu.on("hide", onHide);
15082            menu.on("beforeshow", onBeforeShow);
15083            menu.on("show", onShow);
15084            var g = menu.group;
15085            if(g && menu.events["checkchange"]){
15086                if(!groups[g]){
15087                    groups[g] = [];
15088                }
15089                groups[g].push(menu);
15090                menu.on("checkchange", onCheck);
15091            }
15092        },
15093
15094         /**
15095          * Returns a {@link Roo.menu.Menu} object
15096          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15097          * be used to generate and return a new Menu instance.
15098          */
15099        get : function(menu){
15100            if(typeof menu == "string"){ // menu id
15101                return menus[menu];
15102            }else if(menu.events){  // menu instance
15103                return menu;
15104            }else if(typeof menu.length == 'number'){ // array of menu items?
15105                return new Roo.menu.Menu({items:menu});
15106            }else{ // otherwise, must be a config
15107                return new Roo.menu.Menu(menu);
15108            }
15109        },
15110
15111        // private
15112        unregister : function(menu){
15113            delete menus[menu.id];
15114            menu.un("beforehide", onBeforeHide);
15115            menu.un("hide", onHide);
15116            menu.un("beforeshow", onBeforeShow);
15117            menu.un("show", onShow);
15118            var g = menu.group;
15119            if(g && menu.events["checkchange"]){
15120                groups[g].remove(menu);
15121                menu.un("checkchange", onCheck);
15122            }
15123        },
15124
15125        // private
15126        registerCheckable : function(menuItem){
15127            var g = menuItem.group;
15128            if(g){
15129                if(!groups[g]){
15130                    groups[g] = [];
15131                }
15132                groups[g].push(menuItem);
15133                menuItem.on("beforecheckchange", onBeforeCheck);
15134            }
15135        },
15136
15137        // private
15138        unregisterCheckable : function(menuItem){
15139            var g = menuItem.group;
15140            if(g){
15141                groups[g].remove(menuItem);
15142                menuItem.un("beforecheckchange", onBeforeCheck);
15143            }
15144        }
15145    };
15146 }();/*
15147  * Based on:
15148  * Ext JS Library 1.1.1
15149  * Copyright(c) 2006-2007, Ext JS, LLC.
15150  *
15151  * Originally Released Under LGPL - original licence link has changed is not relivant.
15152  *
15153  * Fork - LGPL
15154  * <script type="text/javascript">
15155  */
15156  
15157
15158 /**
15159  * @class Roo.menu.BaseItem
15160  * @extends Roo.Component
15161  * @abstract
15162  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15163  * management and base configuration options shared by all menu components.
15164  * @constructor
15165  * Creates a new BaseItem
15166  * @param {Object} config Configuration options
15167  */
15168 Roo.menu.BaseItem = function(config){
15169     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15170
15171     this.addEvents({
15172         /**
15173          * @event click
15174          * Fires when this item is clicked
15175          * @param {Roo.menu.BaseItem} this
15176          * @param {Roo.EventObject} e
15177          */
15178         click: true,
15179         /**
15180          * @event activate
15181          * Fires when this item is activated
15182          * @param {Roo.menu.BaseItem} this
15183          */
15184         activate : true,
15185         /**
15186          * @event deactivate
15187          * Fires when this item is deactivated
15188          * @param {Roo.menu.BaseItem} this
15189          */
15190         deactivate : true
15191     });
15192
15193     if(this.handler){
15194         this.on("click", this.handler, this.scope, true);
15195     }
15196 };
15197
15198 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15199     /**
15200      * @cfg {Function} handler
15201      * A function that will handle the click event of this menu item (defaults to undefined)
15202      */
15203     /**
15204      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15205      */
15206     canActivate : false,
15207     
15208      /**
15209      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15210      */
15211     hidden: false,
15212     
15213     /**
15214      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15215      */
15216     activeClass : "x-menu-item-active",
15217     /**
15218      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15219      */
15220     hideOnClick : true,
15221     /**
15222      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15223      */
15224     hideDelay : 100,
15225
15226     // private
15227     ctype: "Roo.menu.BaseItem",
15228
15229     // private
15230     actionMode : "container",
15231
15232     // private
15233     render : function(container, parentMenu){
15234         this.parentMenu = parentMenu;
15235         Roo.menu.BaseItem.superclass.render.call(this, container);
15236         this.container.menuItemId = this.id;
15237     },
15238
15239     // private
15240     onRender : function(container, position){
15241         this.el = Roo.get(this.el);
15242         container.dom.appendChild(this.el.dom);
15243     },
15244
15245     // private
15246     onClick : function(e){
15247         if(!this.disabled && this.fireEvent("click", this, e) !== false
15248                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15249             this.handleClick(e);
15250         }else{
15251             e.stopEvent();
15252         }
15253     },
15254
15255     // private
15256     activate : function(){
15257         if(this.disabled){
15258             return false;
15259         }
15260         var li = this.container;
15261         li.addClass(this.activeClass);
15262         this.region = li.getRegion().adjust(2, 2, -2, -2);
15263         this.fireEvent("activate", this);
15264         return true;
15265     },
15266
15267     // private
15268     deactivate : function(){
15269         this.container.removeClass(this.activeClass);
15270         this.fireEvent("deactivate", this);
15271     },
15272
15273     // private
15274     shouldDeactivate : function(e){
15275         return !this.region || !this.region.contains(e.getPoint());
15276     },
15277
15278     // private
15279     handleClick : function(e){
15280         if(this.hideOnClick){
15281             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15282         }
15283     },
15284
15285     // private
15286     expandMenu : function(autoActivate){
15287         // do nothing
15288     },
15289
15290     // private
15291     hideMenu : function(){
15292         // do nothing
15293     }
15294 });/*
15295  * Based on:
15296  * Ext JS Library 1.1.1
15297  * Copyright(c) 2006-2007, Ext JS, LLC.
15298  *
15299  * Originally Released Under LGPL - original licence link has changed is not relivant.
15300  *
15301  * Fork - LGPL
15302  * <script type="text/javascript">
15303  */
15304  
15305 /**
15306  * @class Roo.menu.Adapter
15307  * @extends Roo.menu.BaseItem
15308  * @abstract
15309  * 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.
15310  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15311  * @constructor
15312  * Creates a new Adapter
15313  * @param {Object} config Configuration options
15314  */
15315 Roo.menu.Adapter = function(component, config){
15316     Roo.menu.Adapter.superclass.constructor.call(this, config);
15317     this.component = component;
15318 };
15319 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15320     // private
15321     canActivate : true,
15322
15323     // private
15324     onRender : function(container, position){
15325         this.component.render(container);
15326         this.el = this.component.getEl();
15327     },
15328
15329     // private
15330     activate : function(){
15331         if(this.disabled){
15332             return false;
15333         }
15334         this.component.focus();
15335         this.fireEvent("activate", this);
15336         return true;
15337     },
15338
15339     // private
15340     deactivate : function(){
15341         this.fireEvent("deactivate", this);
15342     },
15343
15344     // private
15345     disable : function(){
15346         this.component.disable();
15347         Roo.menu.Adapter.superclass.disable.call(this);
15348     },
15349
15350     // private
15351     enable : function(){
15352         this.component.enable();
15353         Roo.menu.Adapter.superclass.enable.call(this);
15354     }
15355 });/*
15356  * Based on:
15357  * Ext JS Library 1.1.1
15358  * Copyright(c) 2006-2007, Ext JS, LLC.
15359  *
15360  * Originally Released Under LGPL - original licence link has changed is not relivant.
15361  *
15362  * Fork - LGPL
15363  * <script type="text/javascript">
15364  */
15365
15366 /**
15367  * @class Roo.menu.TextItem
15368  * @extends Roo.menu.BaseItem
15369  * Adds a static text string to a menu, usually used as either a heading or group separator.
15370  * Note: old style constructor with text is still supported.
15371  * 
15372  * @constructor
15373  * Creates a new TextItem
15374  * @param {Object} cfg Configuration
15375  */
15376 Roo.menu.TextItem = function(cfg){
15377     if (typeof(cfg) == 'string') {
15378         this.text = cfg;
15379     } else {
15380         Roo.apply(this,cfg);
15381     }
15382     
15383     Roo.menu.TextItem.superclass.constructor.call(this);
15384 };
15385
15386 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15387     /**
15388      * @cfg {String} text Text to show on item.
15389      */
15390     text : '',
15391     
15392     /**
15393      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15394      */
15395     hideOnClick : false,
15396     /**
15397      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15398      */
15399     itemCls : "x-menu-text",
15400
15401     // private
15402     onRender : function(){
15403         var s = document.createElement("span");
15404         s.className = this.itemCls;
15405         s.innerHTML = this.text;
15406         this.el = s;
15407         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15408     }
15409 });/*
15410  * Based on:
15411  * Ext JS Library 1.1.1
15412  * Copyright(c) 2006-2007, Ext JS, LLC.
15413  *
15414  * Originally Released Under LGPL - original licence link has changed is not relivant.
15415  *
15416  * Fork - LGPL
15417  * <script type="text/javascript">
15418  */
15419
15420 /**
15421  * @class Roo.menu.Separator
15422  * @extends Roo.menu.BaseItem
15423  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15424  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15425  * @constructor
15426  * @param {Object} config Configuration options
15427  */
15428 Roo.menu.Separator = function(config){
15429     Roo.menu.Separator.superclass.constructor.call(this, config);
15430 };
15431
15432 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15433     /**
15434      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15435      */
15436     itemCls : "x-menu-sep",
15437     /**
15438      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15439      */
15440     hideOnClick : false,
15441
15442     // private
15443     onRender : function(li){
15444         var s = document.createElement("span");
15445         s.className = this.itemCls;
15446         s.innerHTML = "&#160;";
15447         this.el = s;
15448         li.addClass("x-menu-sep-li");
15449         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15450     }
15451 });/*
15452  * Based on:
15453  * Ext JS Library 1.1.1
15454  * Copyright(c) 2006-2007, Ext JS, LLC.
15455  *
15456  * Originally Released Under LGPL - original licence link has changed is not relivant.
15457  *
15458  * Fork - LGPL
15459  * <script type="text/javascript">
15460  */
15461 /**
15462  * @class Roo.menu.Item
15463  * @extends Roo.menu.BaseItem
15464  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15465  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15466  * activation and click handling.
15467  * @constructor
15468  * Creates a new Item
15469  * @param {Object} config Configuration options
15470  */
15471 Roo.menu.Item = function(config){
15472     Roo.menu.Item.superclass.constructor.call(this, config);
15473     if(this.menu){
15474         this.menu = Roo.menu.MenuMgr.get(this.menu);
15475     }
15476 };
15477 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15478     /**
15479      * @cfg {Roo.menu.Menu} menu
15480      * A Sub menu
15481      */
15482     /**
15483      * @cfg {String} text
15484      * The text to show on the menu item.
15485      */
15486     text: '',
15487      /**
15488      * @cfg {String} HTML to render in menu
15489      * The text to show on the menu item (HTML version).
15490      */
15491     html: '',
15492     /**
15493      * @cfg {String} icon
15494      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15495      */
15496     icon: undefined,
15497     /**
15498      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15499      */
15500     itemCls : "x-menu-item",
15501     /**
15502      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15503      */
15504     canActivate : true,
15505     /**
15506      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15507      */
15508     showDelay: 200,
15509     // doc'd in BaseItem
15510     hideDelay: 200,
15511
15512     // private
15513     ctype: "Roo.menu.Item",
15514     
15515     // private
15516     onRender : function(container, position){
15517         var el = document.createElement("a");
15518         el.hideFocus = true;
15519         el.unselectable = "on";
15520         el.href = this.href || "#";
15521         if(this.hrefTarget){
15522             el.target = this.hrefTarget;
15523         }
15524         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15525         
15526         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15527         
15528         el.innerHTML = String.format(
15529                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15530                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15531         this.el = el;
15532         Roo.menu.Item.superclass.onRender.call(this, container, position);
15533     },
15534
15535     /**
15536      * Sets the text to display in this menu item
15537      * @param {String} text The text to display
15538      * @param {Boolean} isHTML true to indicate text is pure html.
15539      */
15540     setText : function(text, isHTML){
15541         if (isHTML) {
15542             this.html = text;
15543         } else {
15544             this.text = text;
15545             this.html = '';
15546         }
15547         if(this.rendered){
15548             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15549      
15550             this.el.update(String.format(
15551                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15552                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15553             this.parentMenu.autoWidth();
15554         }
15555     },
15556
15557     // private
15558     handleClick : function(e){
15559         if(!this.href){ // if no link defined, stop the event automatically
15560             e.stopEvent();
15561         }
15562         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15563     },
15564
15565     // private
15566     activate : function(autoExpand){
15567         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15568             this.focus();
15569             if(autoExpand){
15570                 this.expandMenu();
15571             }
15572         }
15573         return true;
15574     },
15575
15576     // private
15577     shouldDeactivate : function(e){
15578         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15579             if(this.menu && this.menu.isVisible()){
15580                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15581             }
15582             return true;
15583         }
15584         return false;
15585     },
15586
15587     // private
15588     deactivate : function(){
15589         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15590         this.hideMenu();
15591     },
15592
15593     // private
15594     expandMenu : function(autoActivate){
15595         if(!this.disabled && this.menu){
15596             clearTimeout(this.hideTimer);
15597             delete this.hideTimer;
15598             if(!this.menu.isVisible() && !this.showTimer){
15599                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15600             }else if (this.menu.isVisible() && autoActivate){
15601                 this.menu.tryActivate(0, 1);
15602             }
15603         }
15604     },
15605
15606     // private
15607     deferExpand : function(autoActivate){
15608         delete this.showTimer;
15609         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15610         if(autoActivate){
15611             this.menu.tryActivate(0, 1);
15612         }
15613     },
15614
15615     // private
15616     hideMenu : function(){
15617         clearTimeout(this.showTimer);
15618         delete this.showTimer;
15619         if(!this.hideTimer && this.menu && this.menu.isVisible()){
15620             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15621         }
15622     },
15623
15624     // private
15625     deferHide : function(){
15626         delete this.hideTimer;
15627         this.menu.hide();
15628     }
15629 });/*
15630  * Based on:
15631  * Ext JS Library 1.1.1
15632  * Copyright(c) 2006-2007, Ext JS, LLC.
15633  *
15634  * Originally Released Under LGPL - original licence link has changed is not relivant.
15635  *
15636  * Fork - LGPL
15637  * <script type="text/javascript">
15638  */
15639  
15640 /**
15641  * @class Roo.menu.CheckItem
15642  * @extends Roo.menu.Item
15643  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
15644  * @constructor
15645  * Creates a new CheckItem
15646  * @param {Object} config Configuration options
15647  */
15648 Roo.menu.CheckItem = function(config){
15649     Roo.menu.CheckItem.superclass.constructor.call(this, config);
15650     this.addEvents({
15651         /**
15652          * @event beforecheckchange
15653          * Fires before the checked value is set, providing an opportunity to cancel if needed
15654          * @param {Roo.menu.CheckItem} this
15655          * @param {Boolean} checked The new checked value that will be set
15656          */
15657         "beforecheckchange" : true,
15658         /**
15659          * @event checkchange
15660          * Fires after the checked value has been set
15661          * @param {Roo.menu.CheckItem} this
15662          * @param {Boolean} checked The checked value that was set
15663          */
15664         "checkchange" : true
15665     });
15666     if(this.checkHandler){
15667         this.on('checkchange', this.checkHandler, this.scope);
15668     }
15669 };
15670 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
15671     /**
15672      * @cfg {String} group
15673      * All check items with the same group name will automatically be grouped into a single-select
15674      * radio button group (defaults to '')
15675      */
15676     /**
15677      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
15678      */
15679     itemCls : "x-menu-item x-menu-check-item",
15680     /**
15681      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
15682      */
15683     groupClass : "x-menu-group-item",
15684
15685     /**
15686      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
15687      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
15688      * initialized with checked = true will be rendered as checked.
15689      */
15690     checked: false,
15691
15692     // private
15693     ctype: "Roo.menu.CheckItem",
15694
15695     // private
15696     onRender : function(c){
15697         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
15698         if(this.group){
15699             this.el.addClass(this.groupClass);
15700         }
15701         Roo.menu.MenuMgr.registerCheckable(this);
15702         if(this.checked){
15703             this.checked = false;
15704             this.setChecked(true, true);
15705         }
15706     },
15707
15708     // private
15709     destroy : function(){
15710         if(this.rendered){
15711             Roo.menu.MenuMgr.unregisterCheckable(this);
15712         }
15713         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
15714     },
15715
15716     /**
15717      * Set the checked state of this item
15718      * @param {Boolean} checked The new checked value
15719      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
15720      */
15721     setChecked : function(state, suppressEvent){
15722         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
15723             if(this.container){
15724                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
15725             }
15726             this.checked = state;
15727             if(suppressEvent !== true){
15728                 this.fireEvent("checkchange", this, state);
15729             }
15730         }
15731     },
15732
15733     // private
15734     handleClick : function(e){
15735        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
15736            this.setChecked(!this.checked);
15737        }
15738        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
15739     }
15740 });/*
15741  * Based on:
15742  * Ext JS Library 1.1.1
15743  * Copyright(c) 2006-2007, Ext JS, LLC.
15744  *
15745  * Originally Released Under LGPL - original licence link has changed is not relivant.
15746  *
15747  * Fork - LGPL
15748  * <script type="text/javascript">
15749  */
15750  
15751 /**
15752  * @class Roo.menu.DateItem
15753  * @extends Roo.menu.Adapter
15754  * A menu item that wraps the {@link Roo.DatPicker} component.
15755  * @constructor
15756  * Creates a new DateItem
15757  * @param {Object} config Configuration options
15758  */
15759 Roo.menu.DateItem = function(config){
15760     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
15761     /** The Roo.DatePicker object @type Roo.DatePicker */
15762     this.picker = this.component;
15763     this.addEvents({select: true});
15764     
15765     this.picker.on("render", function(picker){
15766         picker.getEl().swallowEvent("click");
15767         picker.container.addClass("x-menu-date-item");
15768     });
15769
15770     this.picker.on("select", this.onSelect, this);
15771 };
15772
15773 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
15774     // private
15775     onSelect : function(picker, date){
15776         this.fireEvent("select", this, date, picker);
15777         Roo.menu.DateItem.superclass.handleClick.call(this);
15778     }
15779 });/*
15780  * Based on:
15781  * Ext JS Library 1.1.1
15782  * Copyright(c) 2006-2007, Ext JS, LLC.
15783  *
15784  * Originally Released Under LGPL - original licence link has changed is not relivant.
15785  *
15786  * Fork - LGPL
15787  * <script type="text/javascript">
15788  */
15789  
15790 /**
15791  * @class Roo.menu.ColorItem
15792  * @extends Roo.menu.Adapter
15793  * A menu item that wraps the {@link Roo.ColorPalette} component.
15794  * @constructor
15795  * Creates a new ColorItem
15796  * @param {Object} config Configuration options
15797  */
15798 Roo.menu.ColorItem = function(config){
15799     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
15800     /** The Roo.ColorPalette object @type Roo.ColorPalette */
15801     this.palette = this.component;
15802     this.relayEvents(this.palette, ["select"]);
15803     if(this.selectHandler){
15804         this.on('select', this.selectHandler, this.scope);
15805     }
15806 };
15807 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
15808  * Based on:
15809  * Ext JS Library 1.1.1
15810  * Copyright(c) 2006-2007, Ext JS, LLC.
15811  *
15812  * Originally Released Under LGPL - original licence link has changed is not relivant.
15813  *
15814  * Fork - LGPL
15815  * <script type="text/javascript">
15816  */
15817  
15818
15819 /**
15820  * @class Roo.menu.DateMenu
15821  * @extends Roo.menu.Menu
15822  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
15823  * @constructor
15824  * Creates a new DateMenu
15825  * @param {Object} config Configuration options
15826  */
15827 Roo.menu.DateMenu = function(config){
15828     Roo.menu.DateMenu.superclass.constructor.call(this, config);
15829     this.plain = true;
15830     var di = new Roo.menu.DateItem(config);
15831     this.add(di);
15832     /**
15833      * The {@link Roo.DatePicker} instance for this DateMenu
15834      * @type DatePicker
15835      */
15836     this.picker = di.picker;
15837     /**
15838      * @event select
15839      * @param {DatePicker} picker
15840      * @param {Date} date
15841      */
15842     this.relayEvents(di, ["select"]);
15843     this.on('beforeshow', function(){
15844         if(this.picker){
15845             this.picker.hideMonthPicker(false);
15846         }
15847     }, this);
15848 };
15849 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
15850     cls:'x-date-menu'
15851 });/*
15852  * Based on:
15853  * Ext JS Library 1.1.1
15854  * Copyright(c) 2006-2007, Ext JS, LLC.
15855  *
15856  * Originally Released Under LGPL - original licence link has changed is not relivant.
15857  *
15858  * Fork - LGPL
15859  * <script type="text/javascript">
15860  */
15861  
15862
15863 /**
15864  * @class Roo.menu.ColorMenu
15865  * @extends Roo.menu.Menu
15866  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
15867  * @constructor
15868  * Creates a new ColorMenu
15869  * @param {Object} config Configuration options
15870  */
15871 Roo.menu.ColorMenu = function(config){
15872     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
15873     this.plain = true;
15874     var ci = new Roo.menu.ColorItem(config);
15875     this.add(ci);
15876     /**
15877      * The {@link Roo.ColorPalette} instance for this ColorMenu
15878      * @type ColorPalette
15879      */
15880     this.palette = ci.palette;
15881     /**
15882      * @event select
15883      * @param {ColorPalette} palette
15884      * @param {String} color
15885      */
15886     this.relayEvents(ci, ["select"]);
15887 };
15888 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
15889  * Based on:
15890  * Ext JS Library 1.1.1
15891  * Copyright(c) 2006-2007, Ext JS, LLC.
15892  *
15893  * Originally Released Under LGPL - original licence link has changed is not relivant.
15894  *
15895  * Fork - LGPL
15896  * <script type="text/javascript">
15897  */
15898  
15899 /**
15900  * @class Roo.form.TextItem
15901  * @extends Roo.BoxComponent
15902  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15903  * @constructor
15904  * Creates a new TextItem
15905  * @param {Object} config Configuration options
15906  */
15907 Roo.form.TextItem = function(config){
15908     Roo.form.TextItem.superclass.constructor.call(this, config);
15909 };
15910
15911 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
15912     
15913     /**
15914      * @cfg {String} tag the tag for this item (default div)
15915      */
15916     tag : 'div',
15917     /**
15918      * @cfg {String} html the content for this item
15919      */
15920     html : '',
15921     
15922     getAutoCreate : function()
15923     {
15924         var cfg = {
15925             id: this.id,
15926             tag: this.tag,
15927             html: this.html,
15928             cls: 'x-form-item'
15929         };
15930         
15931         return cfg;
15932         
15933     },
15934     
15935     onRender : function(ct, position)
15936     {
15937         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
15938         
15939         if(!this.el){
15940             var cfg = this.getAutoCreate();
15941             if(!cfg.name){
15942                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
15943             }
15944             if (!cfg.name.length) {
15945                 delete cfg.name;
15946             }
15947             this.el = ct.createChild(cfg, position);
15948         }
15949     },
15950     /*
15951      * setHTML
15952      * @param {String} html update the Contents of the element.
15953      */
15954     setHTML : function(html)
15955     {
15956         this.fieldEl.dom.innerHTML = html;
15957     }
15958     
15959 });/*
15960  * Based on:
15961  * Ext JS Library 1.1.1
15962  * Copyright(c) 2006-2007, Ext JS, LLC.
15963  *
15964  * Originally Released Under LGPL - original licence link has changed is not relivant.
15965  *
15966  * Fork - LGPL
15967  * <script type="text/javascript">
15968  */
15969  
15970 /**
15971  * @class Roo.form.Field
15972  * @extends Roo.BoxComponent
15973  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15974  * @constructor
15975  * Creates a new Field
15976  * @param {Object} config Configuration options
15977  */
15978 Roo.form.Field = function(config){
15979     Roo.form.Field.superclass.constructor.call(this, config);
15980 };
15981
15982 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
15983     /**
15984      * @cfg {String} fieldLabel Label to use when rendering a form.
15985      */
15986        /**
15987      * @cfg {String} qtip Mouse over tip
15988      */
15989      
15990     /**
15991      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
15992      */
15993     invalidClass : "x-form-invalid",
15994     /**
15995      * @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")
15996      */
15997     invalidText : "The value in this field is invalid",
15998     /**
15999      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16000      */
16001     focusClass : "x-form-focus",
16002     /**
16003      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16004       automatic validation (defaults to "keyup").
16005      */
16006     validationEvent : "keyup",
16007     /**
16008      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16009      */
16010     validateOnBlur : true,
16011     /**
16012      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16013      */
16014     validationDelay : 250,
16015     /**
16016      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16017      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16018      */
16019     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16020     /**
16021      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16022      */
16023     fieldClass : "x-form-field",
16024     /**
16025      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16026      *<pre>
16027 Value         Description
16028 -----------   ----------------------------------------------------------------------
16029 qtip          Display a quick tip when the user hovers over the field
16030 title         Display a default browser title attribute popup
16031 under         Add a block div beneath the field containing the error text
16032 side          Add an error icon to the right of the field with a popup on hover
16033 [element id]  Add the error text directly to the innerHTML of the specified element
16034 </pre>
16035      */
16036     msgTarget : 'qtip',
16037     /**
16038      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16039      */
16040     msgFx : 'normal',
16041
16042     /**
16043      * @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.
16044      */
16045     readOnly : false,
16046
16047     /**
16048      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16049      */
16050     disabled : false,
16051
16052     /**
16053      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16054      */
16055     inputType : undefined,
16056     
16057     /**
16058      * @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).
16059          */
16060         tabIndex : undefined,
16061         
16062     // private
16063     isFormField : true,
16064
16065     // private
16066     hasFocus : false,
16067     /**
16068      * @property {Roo.Element} fieldEl
16069      * Element Containing the rendered Field (with label etc.)
16070      */
16071     /**
16072      * @cfg {Mixed} value A value to initialize this field with.
16073      */
16074     value : undefined,
16075
16076     /**
16077      * @cfg {String} name The field's HTML name attribute.
16078      */
16079     /**
16080      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16081      */
16082     // private
16083     loadedValue : false,
16084      
16085      
16086         // private ??
16087         initComponent : function(){
16088         Roo.form.Field.superclass.initComponent.call(this);
16089         this.addEvents({
16090             /**
16091              * @event focus
16092              * Fires when this field receives input focus.
16093              * @param {Roo.form.Field} this
16094              */
16095             focus : true,
16096             /**
16097              * @event blur
16098              * Fires when this field loses input focus.
16099              * @param {Roo.form.Field} this
16100              */
16101             blur : true,
16102             /**
16103              * @event specialkey
16104              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16105              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16106              * @param {Roo.form.Field} this
16107              * @param {Roo.EventObject} e The event object
16108              */
16109             specialkey : true,
16110             /**
16111              * @event change
16112              * Fires just before the field blurs if the field value has changed.
16113              * @param {Roo.form.Field} this
16114              * @param {Mixed} newValue The new value
16115              * @param {Mixed} oldValue The original value
16116              */
16117             change : true,
16118             /**
16119              * @event invalid
16120              * Fires after the field has been marked as invalid.
16121              * @param {Roo.form.Field} this
16122              * @param {String} msg The validation message
16123              */
16124             invalid : true,
16125             /**
16126              * @event valid
16127              * Fires after the field has been validated with no errors.
16128              * @param {Roo.form.Field} this
16129              */
16130             valid : true,
16131              /**
16132              * @event keyup
16133              * Fires after the key up
16134              * @param {Roo.form.Field} this
16135              * @param {Roo.EventObject}  e The event Object
16136              */
16137             keyup : true
16138         });
16139     },
16140
16141     /**
16142      * Returns the name attribute of the field if available
16143      * @return {String} name The field name
16144      */
16145     getName: function(){
16146          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16147     },
16148
16149     // private
16150     onRender : function(ct, position){
16151         Roo.form.Field.superclass.onRender.call(this, ct, position);
16152         if(!this.el){
16153             var cfg = this.getAutoCreate();
16154             if(!cfg.name){
16155                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16156             }
16157             if (!cfg.name.length) {
16158                 delete cfg.name;
16159             }
16160             if(this.inputType){
16161                 cfg.type = this.inputType;
16162             }
16163             this.el = ct.createChild(cfg, position);
16164         }
16165         var type = this.el.dom.type;
16166         if(type){
16167             if(type == 'password'){
16168                 type = 'text';
16169             }
16170             this.el.addClass('x-form-'+type);
16171         }
16172         if(this.readOnly){
16173             this.el.dom.readOnly = true;
16174         }
16175         if(this.tabIndex !== undefined){
16176             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16177         }
16178
16179         this.el.addClass([this.fieldClass, this.cls]);
16180         this.initValue();
16181     },
16182
16183     /**
16184      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16185      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16186      * @return {Roo.form.Field} this
16187      */
16188     applyTo : function(target){
16189         this.allowDomMove = false;
16190         this.el = Roo.get(target);
16191         this.render(this.el.dom.parentNode);
16192         return this;
16193     },
16194
16195     // private
16196     initValue : function(){
16197         if(this.value !== undefined){
16198             this.setValue(this.value);
16199         }else if(this.el.dom.value.length > 0){
16200             this.setValue(this.el.dom.value);
16201         }
16202     },
16203
16204     /**
16205      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16206      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16207      */
16208     isDirty : function() {
16209         if(this.disabled) {
16210             return false;
16211         }
16212         return String(this.getValue()) !== String(this.originalValue);
16213     },
16214
16215     /**
16216      * stores the current value in loadedValue
16217      */
16218     resetHasChanged : function()
16219     {
16220         this.loadedValue = String(this.getValue());
16221     },
16222     /**
16223      * checks the current value against the 'loaded' value.
16224      * Note - will return false if 'resetHasChanged' has not been called first.
16225      */
16226     hasChanged : function()
16227     {
16228         if(this.disabled || this.readOnly) {
16229             return false;
16230         }
16231         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16232     },
16233     
16234     
16235     
16236     // private
16237     afterRender : function(){
16238         Roo.form.Field.superclass.afterRender.call(this);
16239         this.initEvents();
16240     },
16241
16242     // private
16243     fireKey : function(e){
16244         //Roo.log('field ' + e.getKey());
16245         if(e.isNavKeyPress()){
16246             this.fireEvent("specialkey", this, e);
16247         }
16248     },
16249
16250     /**
16251      * Resets the current field value to the originally loaded value and clears any validation messages
16252      */
16253     reset : function(){
16254         this.setValue(this.resetValue);
16255         this.originalValue = this.getValue();
16256         this.clearInvalid();
16257     },
16258
16259     // private
16260     initEvents : function(){
16261         // safari killled keypress - so keydown is now used..
16262         this.el.on("keydown" , this.fireKey,  this);
16263         this.el.on("focus", this.onFocus,  this);
16264         this.el.on("blur", this.onBlur,  this);
16265         this.el.relayEvent('keyup', this);
16266
16267         // reference to original value for reset
16268         this.originalValue = this.getValue();
16269         this.resetValue =  this.getValue();
16270     },
16271
16272     // private
16273     onFocus : function(){
16274         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16275             this.el.addClass(this.focusClass);
16276         }
16277         if(!this.hasFocus){
16278             this.hasFocus = true;
16279             this.startValue = this.getValue();
16280             this.fireEvent("focus", this);
16281         }
16282     },
16283
16284     beforeBlur : Roo.emptyFn,
16285
16286     // private
16287     onBlur : function(){
16288         this.beforeBlur();
16289         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16290             this.el.removeClass(this.focusClass);
16291         }
16292         this.hasFocus = false;
16293         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16294             this.validate();
16295         }
16296         var v = this.getValue();
16297         if(String(v) !== String(this.startValue)){
16298             this.fireEvent('change', this, v, this.startValue);
16299         }
16300         this.fireEvent("blur", this);
16301     },
16302
16303     /**
16304      * Returns whether or not the field value is currently valid
16305      * @param {Boolean} preventMark True to disable marking the field invalid
16306      * @return {Boolean} True if the value is valid, else false
16307      */
16308     isValid : function(preventMark){
16309         if(this.disabled){
16310             return true;
16311         }
16312         var restore = this.preventMark;
16313         this.preventMark = preventMark === true;
16314         var v = this.validateValue(this.processValue(this.getRawValue()));
16315         this.preventMark = restore;
16316         return v;
16317     },
16318
16319     /**
16320      * Validates the field value
16321      * @return {Boolean} True if the value is valid, else false
16322      */
16323     validate : function(){
16324         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16325             this.clearInvalid();
16326             return true;
16327         }
16328         return false;
16329     },
16330
16331     processValue : function(value){
16332         return value;
16333     },
16334
16335     // private
16336     // Subclasses should provide the validation implementation by overriding this
16337     validateValue : function(value){
16338         return true;
16339     },
16340
16341     /**
16342      * Mark this field as invalid
16343      * @param {String} msg The validation message
16344      */
16345     markInvalid : function(msg){
16346         if(!this.rendered || this.preventMark){ // not rendered
16347             return;
16348         }
16349         
16350         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16351         
16352         obj.el.addClass(this.invalidClass);
16353         msg = msg || this.invalidText;
16354         switch(this.msgTarget){
16355             case 'qtip':
16356                 obj.el.dom.qtip = msg;
16357                 obj.el.dom.qclass = 'x-form-invalid-tip';
16358                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16359                     Roo.QuickTips.enable();
16360                 }
16361                 break;
16362             case 'title':
16363                 this.el.dom.title = msg;
16364                 break;
16365             case 'under':
16366                 if(!this.errorEl){
16367                     var elp = this.el.findParent('.x-form-element', 5, true);
16368                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16369                     this.errorEl.setWidth(elp.getWidth(true)-20);
16370                 }
16371                 this.errorEl.update(msg);
16372                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16373                 break;
16374             case 'side':
16375                 if(!this.errorIcon){
16376                     var elp = this.el.findParent('.x-form-element', 5, true);
16377                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16378                 }
16379                 this.alignErrorIcon();
16380                 this.errorIcon.dom.qtip = msg;
16381                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16382                 this.errorIcon.show();
16383                 this.on('resize', this.alignErrorIcon, this);
16384                 break;
16385             default:
16386                 var t = Roo.getDom(this.msgTarget);
16387                 t.innerHTML = msg;
16388                 t.style.display = this.msgDisplay;
16389                 break;
16390         }
16391         this.fireEvent('invalid', this, msg);
16392     },
16393
16394     // private
16395     alignErrorIcon : function(){
16396         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16397     },
16398
16399     /**
16400      * Clear any invalid styles/messages for this field
16401      */
16402     clearInvalid : function(){
16403         if(!this.rendered || this.preventMark){ // not rendered
16404             return;
16405         }
16406         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16407         
16408         obj.el.removeClass(this.invalidClass);
16409         switch(this.msgTarget){
16410             case 'qtip':
16411                 obj.el.dom.qtip = '';
16412                 break;
16413             case 'title':
16414                 this.el.dom.title = '';
16415                 break;
16416             case 'under':
16417                 if(this.errorEl){
16418                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16419                 }
16420                 break;
16421             case 'side':
16422                 if(this.errorIcon){
16423                     this.errorIcon.dom.qtip = '';
16424                     this.errorIcon.hide();
16425                     this.un('resize', this.alignErrorIcon, this);
16426                 }
16427                 break;
16428             default:
16429                 var t = Roo.getDom(this.msgTarget);
16430                 t.innerHTML = '';
16431                 t.style.display = 'none';
16432                 break;
16433         }
16434         this.fireEvent('valid', this);
16435     },
16436
16437     /**
16438      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16439      * @return {Mixed} value The field value
16440      */
16441     getRawValue : function(){
16442         var v = this.el.getValue();
16443         
16444         return v;
16445     },
16446
16447     /**
16448      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16449      * @return {Mixed} value The field value
16450      */
16451     getValue : function(){
16452         var v = this.el.getValue();
16453          
16454         return v;
16455     },
16456
16457     /**
16458      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16459      * @param {Mixed} value The value to set
16460      */
16461     setRawValue : function(v){
16462         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16463     },
16464
16465     /**
16466      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16467      * @param {Mixed} value The value to set
16468      */
16469     setValue : function(v){
16470         this.value = v;
16471         if(this.rendered){
16472             this.el.dom.value = (v === null || v === undefined ? '' : v);
16473              this.validate();
16474         }
16475     },
16476
16477     adjustSize : function(w, h){
16478         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16479         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16480         return s;
16481     },
16482
16483     adjustWidth : function(tag, w){
16484         tag = tag.toLowerCase();
16485         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16486             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16487                 if(tag == 'input'){
16488                     return w + 2;
16489                 }
16490                 if(tag == 'textarea'){
16491                     return w-2;
16492                 }
16493             }else if(Roo.isOpera){
16494                 if(tag == 'input'){
16495                     return w + 2;
16496                 }
16497                 if(tag == 'textarea'){
16498                     return w-2;
16499                 }
16500             }
16501         }
16502         return w;
16503     }
16504 });
16505
16506
16507 // anything other than normal should be considered experimental
16508 Roo.form.Field.msgFx = {
16509     normal : {
16510         show: function(msgEl, f){
16511             msgEl.setDisplayed('block');
16512         },
16513
16514         hide : function(msgEl, f){
16515             msgEl.setDisplayed(false).update('');
16516         }
16517     },
16518
16519     slide : {
16520         show: function(msgEl, f){
16521             msgEl.slideIn('t', {stopFx:true});
16522         },
16523
16524         hide : function(msgEl, f){
16525             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16526         }
16527     },
16528
16529     slideRight : {
16530         show: function(msgEl, f){
16531             msgEl.fixDisplay();
16532             msgEl.alignTo(f.el, 'tl-tr');
16533             msgEl.slideIn('l', {stopFx:true});
16534         },
16535
16536         hide : function(msgEl, f){
16537             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16538         }
16539     }
16540 };/*
16541  * Based on:
16542  * Ext JS Library 1.1.1
16543  * Copyright(c) 2006-2007, Ext JS, LLC.
16544  *
16545  * Originally Released Under LGPL - original licence link has changed is not relivant.
16546  *
16547  * Fork - LGPL
16548  * <script type="text/javascript">
16549  */
16550  
16551
16552 /**
16553  * @class Roo.form.TextField
16554  * @extends Roo.form.Field
16555  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16556  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16557  * @constructor
16558  * Creates a new TextField
16559  * @param {Object} config Configuration options
16560  */
16561 Roo.form.TextField = function(config){
16562     Roo.form.TextField.superclass.constructor.call(this, config);
16563     this.addEvents({
16564         /**
16565          * @event autosize
16566          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16567          * according to the default logic, but this event provides a hook for the developer to apply additional
16568          * logic at runtime to resize the field if needed.
16569              * @param {Roo.form.Field} this This text field
16570              * @param {Number} width The new field width
16571              */
16572         autosize : true
16573     });
16574 };
16575
16576 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16577     /**
16578      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16579      */
16580     grow : false,
16581     /**
16582      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16583      */
16584     growMin : 30,
16585     /**
16586      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16587      */
16588     growMax : 800,
16589     /**
16590      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16591      */
16592     vtype : null,
16593     /**
16594      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16595      */
16596     maskRe : null,
16597     /**
16598      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16599      */
16600     disableKeyFilter : false,
16601     /**
16602      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16603      */
16604     allowBlank : true,
16605     /**
16606      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16607      */
16608     minLength : 0,
16609     /**
16610      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16611      */
16612     maxLength : Number.MAX_VALUE,
16613     /**
16614      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16615      */
16616     minLengthText : "The minimum length for this field is {0}",
16617     /**
16618      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16619      */
16620     maxLengthText : "The maximum length for this field is {0}",
16621     /**
16622      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16623      */
16624     selectOnFocus : false,
16625     /**
16626      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
16627      */    
16628     allowLeadingSpace : false,
16629     /**
16630      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16631      */
16632     blankText : "This field is required",
16633     /**
16634      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16635      * If available, this function will be called only after the basic validators all return true, and will be passed the
16636      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16637      */
16638     validator : null,
16639     /**
16640      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16641      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16642      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
16643      */
16644     regex : null,
16645     /**
16646      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16647      */
16648     regexText : "",
16649     /**
16650      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16651      */
16652     emptyText : null,
16653    
16654
16655     // private
16656     initEvents : function()
16657     {
16658         if (this.emptyText) {
16659             this.el.attr('placeholder', this.emptyText);
16660         }
16661         
16662         Roo.form.TextField.superclass.initEvents.call(this);
16663         if(this.validationEvent == 'keyup'){
16664             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16665             this.el.on('keyup', this.filterValidation, this);
16666         }
16667         else if(this.validationEvent !== false){
16668             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16669         }
16670         
16671         if(this.selectOnFocus){
16672             this.on("focus", this.preFocus, this);
16673         }
16674         if (!this.allowLeadingSpace) {
16675             this.on('blur', this.cleanLeadingSpace, this);
16676         }
16677         
16678         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16679             this.el.on("keypress", this.filterKeys, this);
16680         }
16681         if(this.grow){
16682             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
16683             this.el.on("click", this.autoSize,  this);
16684         }
16685         if(this.el.is('input[type=password]') && Roo.isSafari){
16686             this.el.on('keydown', this.SafariOnKeyDown, this);
16687         }
16688     },
16689
16690     processValue : function(value){
16691         if(this.stripCharsRe){
16692             var newValue = value.replace(this.stripCharsRe, '');
16693             if(newValue !== value){
16694                 this.setRawValue(newValue);
16695                 return newValue;
16696             }
16697         }
16698         return value;
16699     },
16700
16701     filterValidation : function(e){
16702         if(!e.isNavKeyPress()){
16703             this.validationTask.delay(this.validationDelay);
16704         }
16705     },
16706
16707     // private
16708     onKeyUp : function(e){
16709         if(!e.isNavKeyPress()){
16710             this.autoSize();
16711         }
16712     },
16713     // private - clean the leading white space
16714     cleanLeadingSpace : function(e)
16715     {
16716         if ( this.inputType == 'file') {
16717             return;
16718         }
16719         
16720         this.setValue((this.getValue() + '').replace(/^\s+/,''));
16721     },
16722     /**
16723      * Resets the current field value to the originally-loaded value and clears any validation messages.
16724      *  
16725      */
16726     reset : function(){
16727         Roo.form.TextField.superclass.reset.call(this);
16728        
16729     }, 
16730     // private
16731     preFocus : function(){
16732         
16733         if(this.selectOnFocus){
16734             this.el.dom.select();
16735         }
16736     },
16737
16738     
16739     // private
16740     filterKeys : function(e){
16741         var k = e.getKey();
16742         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
16743             return;
16744         }
16745         var c = e.getCharCode(), cc = String.fromCharCode(c);
16746         if(Roo.isIE && (e.isSpecialKey() || !cc)){
16747             return;
16748         }
16749         if(!this.maskRe.test(cc)){
16750             e.stopEvent();
16751         }
16752     },
16753
16754     setValue : function(v){
16755         
16756         Roo.form.TextField.superclass.setValue.apply(this, arguments);
16757         
16758         this.autoSize();
16759     },
16760
16761     /**
16762      * Validates a value according to the field's validation rules and marks the field as invalid
16763      * if the validation fails
16764      * @param {Mixed} value The value to validate
16765      * @return {Boolean} True if the value is valid, else false
16766      */
16767     validateValue : function(value){
16768         if(value.length < 1)  { // if it's blank
16769              if(this.allowBlank){
16770                 this.clearInvalid();
16771                 return true;
16772              }else{
16773                 this.markInvalid(this.blankText);
16774                 return false;
16775              }
16776         }
16777         if(value.length < this.minLength){
16778             this.markInvalid(String.format(this.minLengthText, this.minLength));
16779             return false;
16780         }
16781         if(value.length > this.maxLength){
16782             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
16783             return false;
16784         }
16785         if(this.vtype){
16786             var vt = Roo.form.VTypes;
16787             if(!vt[this.vtype](value, this)){
16788                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
16789                 return false;
16790             }
16791         }
16792         if(typeof this.validator == "function"){
16793             var msg = this.validator(value);
16794             if(msg !== true){
16795                 this.markInvalid(msg);
16796                 return false;
16797             }
16798         }
16799         if(this.regex && !this.regex.test(value)){
16800             this.markInvalid(this.regexText);
16801             return false;
16802         }
16803         return true;
16804     },
16805
16806     /**
16807      * Selects text in this field
16808      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
16809      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
16810      */
16811     selectText : function(start, end){
16812         var v = this.getRawValue();
16813         if(v.length > 0){
16814             start = start === undefined ? 0 : start;
16815             end = end === undefined ? v.length : end;
16816             var d = this.el.dom;
16817             if(d.setSelectionRange){
16818                 d.setSelectionRange(start, end);
16819             }else if(d.createTextRange){
16820                 var range = d.createTextRange();
16821                 range.moveStart("character", start);
16822                 range.moveEnd("character", v.length-end);
16823                 range.select();
16824             }
16825         }
16826     },
16827
16828     /**
16829      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
16830      * This only takes effect if grow = true, and fires the autosize event.
16831      */
16832     autoSize : function(){
16833         if(!this.grow || !this.rendered){
16834             return;
16835         }
16836         if(!this.metrics){
16837             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
16838         }
16839         var el = this.el;
16840         var v = el.dom.value;
16841         var d = document.createElement('div');
16842         d.appendChild(document.createTextNode(v));
16843         v = d.innerHTML;
16844         d = null;
16845         v += "&#160;";
16846         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
16847         this.el.setWidth(w);
16848         this.fireEvent("autosize", this, w);
16849     },
16850     
16851     // private
16852     SafariOnKeyDown : function(event)
16853     {
16854         // this is a workaround for a password hang bug on chrome/ webkit.
16855         
16856         var isSelectAll = false;
16857         
16858         if(this.el.dom.selectionEnd > 0){
16859             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
16860         }
16861         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
16862             event.preventDefault();
16863             this.setValue('');
16864             return;
16865         }
16866         
16867         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
16868             
16869             event.preventDefault();
16870             // this is very hacky as keydown always get's upper case.
16871             
16872             var cc = String.fromCharCode(event.getCharCode());
16873             
16874             
16875             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
16876             
16877         }
16878         
16879         
16880     }
16881 });/*
16882  * Based on:
16883  * Ext JS Library 1.1.1
16884  * Copyright(c) 2006-2007, Ext JS, LLC.
16885  *
16886  * Originally Released Under LGPL - original licence link has changed is not relivant.
16887  *
16888  * Fork - LGPL
16889  * <script type="text/javascript">
16890  */
16891  
16892 /**
16893  * @class Roo.form.Hidden
16894  * @extends Roo.form.TextField
16895  * Simple Hidden element used on forms 
16896  * 
16897  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
16898  * 
16899  * @constructor
16900  * Creates a new Hidden form element.
16901  * @param {Object} config Configuration options
16902  */
16903
16904
16905
16906 // easy hidden field...
16907 Roo.form.Hidden = function(config){
16908     Roo.form.Hidden.superclass.constructor.call(this, config);
16909 };
16910   
16911 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
16912     fieldLabel:      '',
16913     inputType:      'hidden',
16914     width:          50,
16915     allowBlank:     true,
16916     labelSeparator: '',
16917     hidden:         true,
16918     itemCls :       'x-form-item-display-none'
16919
16920
16921 });
16922
16923
16924 /*
16925  * Based on:
16926  * Ext JS Library 1.1.1
16927  * Copyright(c) 2006-2007, Ext JS, LLC.
16928  *
16929  * Originally Released Under LGPL - original licence link has changed is not relivant.
16930  *
16931  * Fork - LGPL
16932  * <script type="text/javascript">
16933  */
16934  
16935 /**
16936  * @class Roo.form.TriggerField
16937  * @extends Roo.form.TextField
16938  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
16939  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
16940  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
16941  * for which you can provide a custom implementation.  For example:
16942  * <pre><code>
16943 var trigger = new Roo.form.TriggerField();
16944 trigger.onTriggerClick = myTriggerFn;
16945 trigger.applyTo('my-field');
16946 </code></pre>
16947  *
16948  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
16949  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
16950  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
16951  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
16952  * @constructor
16953  * Create a new TriggerField.
16954  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
16955  * to the base TextField)
16956  */
16957 Roo.form.TriggerField = function(config){
16958     this.mimicing = false;
16959     Roo.form.TriggerField.superclass.constructor.call(this, config);
16960 };
16961
16962 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
16963     /**
16964      * @cfg {String} triggerClass A CSS class to apply to the trigger
16965      */
16966     /**
16967      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16968      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
16969      */
16970     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
16971     /**
16972      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
16973      */
16974     hideTrigger:false,
16975
16976     /** @cfg {Boolean} grow @hide */
16977     /** @cfg {Number} growMin @hide */
16978     /** @cfg {Number} growMax @hide */
16979
16980     /**
16981      * @hide 
16982      * @method
16983      */
16984     autoSize: Roo.emptyFn,
16985     // private
16986     monitorTab : true,
16987     // private
16988     deferHeight : true,
16989
16990     
16991     actionMode : 'wrap',
16992     // private
16993     onResize : function(w, h){
16994         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
16995         if(typeof w == 'number'){
16996             var x = w - this.trigger.getWidth();
16997             this.el.setWidth(this.adjustWidth('input', x));
16998             this.trigger.setStyle('left', x+'px');
16999         }
17000     },
17001
17002     // private
17003     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17004
17005     // private
17006     getResizeEl : function(){
17007         return this.wrap;
17008     },
17009
17010     // private
17011     getPositionEl : function(){
17012         return this.wrap;
17013     },
17014
17015     // private
17016     alignErrorIcon : function(){
17017         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17018     },
17019
17020     // private
17021     onRender : function(ct, position){
17022         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17023         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17024         this.trigger = this.wrap.createChild(this.triggerConfig ||
17025                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17026         if(this.hideTrigger){
17027             this.trigger.setDisplayed(false);
17028         }
17029         this.initTrigger();
17030         if(!this.width){
17031             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17032         }
17033     },
17034
17035     // private
17036     initTrigger : function(){
17037         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17038         this.trigger.addClassOnOver('x-form-trigger-over');
17039         this.trigger.addClassOnClick('x-form-trigger-click');
17040     },
17041
17042     // private
17043     onDestroy : function(){
17044         if(this.trigger){
17045             this.trigger.removeAllListeners();
17046             this.trigger.remove();
17047         }
17048         if(this.wrap){
17049             this.wrap.remove();
17050         }
17051         Roo.form.TriggerField.superclass.onDestroy.call(this);
17052     },
17053
17054     // private
17055     onFocus : function(){
17056         Roo.form.TriggerField.superclass.onFocus.call(this);
17057         if(!this.mimicing){
17058             this.wrap.addClass('x-trigger-wrap-focus');
17059             this.mimicing = true;
17060             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17061             if(this.monitorTab){
17062                 this.el.on("keydown", this.checkTab, this);
17063             }
17064         }
17065     },
17066
17067     // private
17068     checkTab : function(e){
17069         if(e.getKey() == e.TAB){
17070             this.triggerBlur();
17071         }
17072     },
17073
17074     // private
17075     onBlur : function(){
17076         // do nothing
17077     },
17078
17079     // private
17080     mimicBlur : function(e, t){
17081         if(!this.wrap.contains(t) && this.validateBlur()){
17082             this.triggerBlur();
17083         }
17084     },
17085
17086     // private
17087     triggerBlur : function(){
17088         this.mimicing = false;
17089         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17090         if(this.monitorTab){
17091             this.el.un("keydown", this.checkTab, this);
17092         }
17093         this.wrap.removeClass('x-trigger-wrap-focus');
17094         Roo.form.TriggerField.superclass.onBlur.call(this);
17095     },
17096
17097     // private
17098     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17099     validateBlur : function(e, t){
17100         return true;
17101     },
17102
17103     // private
17104     onDisable : function(){
17105         Roo.form.TriggerField.superclass.onDisable.call(this);
17106         if(this.wrap){
17107             this.wrap.addClass('x-item-disabled');
17108         }
17109     },
17110
17111     // private
17112     onEnable : function(){
17113         Roo.form.TriggerField.superclass.onEnable.call(this);
17114         if(this.wrap){
17115             this.wrap.removeClass('x-item-disabled');
17116         }
17117     },
17118
17119     // private
17120     onShow : function(){
17121         var ae = this.getActionEl();
17122         
17123         if(ae){
17124             ae.dom.style.display = '';
17125             ae.dom.style.visibility = 'visible';
17126         }
17127     },
17128
17129     // private
17130     
17131     onHide : function(){
17132         var ae = this.getActionEl();
17133         ae.dom.style.display = 'none';
17134     },
17135
17136     /**
17137      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17138      * by an implementing function.
17139      * @method
17140      * @param {EventObject} e
17141      */
17142     onTriggerClick : Roo.emptyFn
17143 });
17144
17145 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17146 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17147 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17148 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17149     initComponent : function(){
17150         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17151
17152         this.triggerConfig = {
17153             tag:'span', cls:'x-form-twin-triggers', cn:[
17154             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17155             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17156         ]};
17157     },
17158
17159     getTrigger : function(index){
17160         return this.triggers[index];
17161     },
17162
17163     initTrigger : function(){
17164         var ts = this.trigger.select('.x-form-trigger', true);
17165         this.wrap.setStyle('overflow', 'hidden');
17166         var triggerField = this;
17167         ts.each(function(t, all, index){
17168             t.hide = function(){
17169                 var w = triggerField.wrap.getWidth();
17170                 this.dom.style.display = 'none';
17171                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17172             };
17173             t.show = function(){
17174                 var w = triggerField.wrap.getWidth();
17175                 this.dom.style.display = '';
17176                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17177             };
17178             var triggerIndex = 'Trigger'+(index+1);
17179
17180             if(this['hide'+triggerIndex]){
17181                 t.dom.style.display = 'none';
17182             }
17183             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17184             t.addClassOnOver('x-form-trigger-over');
17185             t.addClassOnClick('x-form-trigger-click');
17186         }, this);
17187         this.triggers = ts.elements;
17188     },
17189
17190     onTrigger1Click : Roo.emptyFn,
17191     onTrigger2Click : Roo.emptyFn
17192 });/*
17193  * Based on:
17194  * Ext JS Library 1.1.1
17195  * Copyright(c) 2006-2007, Ext JS, LLC.
17196  *
17197  * Originally Released Under LGPL - original licence link has changed is not relivant.
17198  *
17199  * Fork - LGPL
17200  * <script type="text/javascript">
17201  */
17202  
17203 /**
17204  * @class Roo.form.TextArea
17205  * @extends Roo.form.TextField
17206  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17207  * support for auto-sizing.
17208  * @constructor
17209  * Creates a new TextArea
17210  * @param {Object} config Configuration options
17211  */
17212 Roo.form.TextArea = function(config){
17213     Roo.form.TextArea.superclass.constructor.call(this, config);
17214     // these are provided exchanges for backwards compat
17215     // minHeight/maxHeight were replaced by growMin/growMax to be
17216     // compatible with TextField growing config values
17217     if(this.minHeight !== undefined){
17218         this.growMin = this.minHeight;
17219     }
17220     if(this.maxHeight !== undefined){
17221         this.growMax = this.maxHeight;
17222     }
17223 };
17224
17225 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17226     /**
17227      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17228      */
17229     growMin : 60,
17230     /**
17231      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17232      */
17233     growMax: 1000,
17234     /**
17235      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17236      * in the field (equivalent to setting overflow: hidden, defaults to false)
17237      */
17238     preventScrollbars: false,
17239     /**
17240      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17241      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17242      */
17243
17244     // private
17245     onRender : function(ct, position){
17246         if(!this.el){
17247             this.defaultAutoCreate = {
17248                 tag: "textarea",
17249                 style:"width:300px;height:60px;",
17250                 autocomplete: "new-password"
17251             };
17252         }
17253         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17254         if(this.grow){
17255             this.textSizeEl = Roo.DomHelper.append(document.body, {
17256                 tag: "pre", cls: "x-form-grow-sizer"
17257             });
17258             if(this.preventScrollbars){
17259                 this.el.setStyle("overflow", "hidden");
17260             }
17261             this.el.setHeight(this.growMin);
17262         }
17263     },
17264
17265     onDestroy : function(){
17266         if(this.textSizeEl){
17267             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17268         }
17269         Roo.form.TextArea.superclass.onDestroy.call(this);
17270     },
17271
17272     // private
17273     onKeyUp : function(e){
17274         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17275             this.autoSize();
17276         }
17277     },
17278
17279     /**
17280      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17281      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17282      */
17283     autoSize : function(){
17284         if(!this.grow || !this.textSizeEl){
17285             return;
17286         }
17287         var el = this.el;
17288         var v = el.dom.value;
17289         var ts = this.textSizeEl;
17290
17291         ts.innerHTML = '';
17292         ts.appendChild(document.createTextNode(v));
17293         v = ts.innerHTML;
17294
17295         Roo.fly(ts).setWidth(this.el.getWidth());
17296         if(v.length < 1){
17297             v = "&#160;&#160;";
17298         }else{
17299             if(Roo.isIE){
17300                 v = v.replace(/\n/g, '<p>&#160;</p>');
17301             }
17302             v += "&#160;\n&#160;";
17303         }
17304         ts.innerHTML = v;
17305         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17306         if(h != this.lastHeight){
17307             this.lastHeight = h;
17308             this.el.setHeight(h);
17309             this.fireEvent("autosize", this, h);
17310         }
17311     }
17312 });/*
17313  * Based on:
17314  * Ext JS Library 1.1.1
17315  * Copyright(c) 2006-2007, Ext JS, LLC.
17316  *
17317  * Originally Released Under LGPL - original licence link has changed is not relivant.
17318  *
17319  * Fork - LGPL
17320  * <script type="text/javascript">
17321  */
17322  
17323
17324 /**
17325  * @class Roo.form.NumberField
17326  * @extends Roo.form.TextField
17327  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17328  * @constructor
17329  * Creates a new NumberField
17330  * @param {Object} config Configuration options
17331  */
17332 Roo.form.NumberField = function(config){
17333     Roo.form.NumberField.superclass.constructor.call(this, config);
17334 };
17335
17336 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17337     /**
17338      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17339      */
17340     fieldClass: "x-form-field x-form-num-field",
17341     /**
17342      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17343      */
17344     allowDecimals : true,
17345     /**
17346      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17347      */
17348     decimalSeparator : ".",
17349     /**
17350      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17351      */
17352     decimalPrecision : 2,
17353     /**
17354      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17355      */
17356     allowNegative : true,
17357     /**
17358      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17359      */
17360     minValue : Number.NEGATIVE_INFINITY,
17361     /**
17362      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17363      */
17364     maxValue : Number.MAX_VALUE,
17365     /**
17366      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17367      */
17368     minText : "The minimum value for this field is {0}",
17369     /**
17370      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17371      */
17372     maxText : "The maximum value for this field is {0}",
17373     /**
17374      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17375      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17376      */
17377     nanText : "{0} is not a valid number",
17378
17379     // private
17380     initEvents : function(){
17381         Roo.form.NumberField.superclass.initEvents.call(this);
17382         var allowed = "0123456789";
17383         if(this.allowDecimals){
17384             allowed += this.decimalSeparator;
17385         }
17386         if(this.allowNegative){
17387             allowed += "-";
17388         }
17389         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17390         var keyPress = function(e){
17391             var k = e.getKey();
17392             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17393                 return;
17394             }
17395             var c = e.getCharCode();
17396             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17397                 e.stopEvent();
17398             }
17399         };
17400         this.el.on("keypress", keyPress, this);
17401     },
17402
17403     // private
17404     validateValue : function(value){
17405         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17406             return false;
17407         }
17408         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17409              return true;
17410         }
17411         var num = this.parseValue(value);
17412         if(isNaN(num)){
17413             this.markInvalid(String.format(this.nanText, value));
17414             return false;
17415         }
17416         if(num < this.minValue){
17417             this.markInvalid(String.format(this.minText, this.minValue));
17418             return false;
17419         }
17420         if(num > this.maxValue){
17421             this.markInvalid(String.format(this.maxText, this.maxValue));
17422             return false;
17423         }
17424         return true;
17425     },
17426
17427     getValue : function(){
17428         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17429     },
17430
17431     // private
17432     parseValue : function(value){
17433         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17434         return isNaN(value) ? '' : value;
17435     },
17436
17437     // private
17438     fixPrecision : function(value){
17439         var nan = isNaN(value);
17440         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17441             return nan ? '' : value;
17442         }
17443         return parseFloat(value).toFixed(this.decimalPrecision);
17444     },
17445
17446     setValue : function(v){
17447         v = this.fixPrecision(v);
17448         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17449     },
17450
17451     // private
17452     decimalPrecisionFcn : function(v){
17453         return Math.floor(v);
17454     },
17455
17456     beforeBlur : function(){
17457         var v = this.parseValue(this.getRawValue());
17458         if(v){
17459             this.setValue(v);
17460         }
17461     }
17462 });/*
17463  * Based on:
17464  * Ext JS Library 1.1.1
17465  * Copyright(c) 2006-2007, Ext JS, LLC.
17466  *
17467  * Originally Released Under LGPL - original licence link has changed is not relivant.
17468  *
17469  * Fork - LGPL
17470  * <script type="text/javascript">
17471  */
17472  
17473 /**
17474  * @class Roo.form.DateField
17475  * @extends Roo.form.TriggerField
17476  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17477 * @constructor
17478 * Create a new DateField
17479 * @param {Object} config
17480  */
17481 Roo.form.DateField = function(config)
17482 {
17483     Roo.form.DateField.superclass.constructor.call(this, config);
17484     
17485       this.addEvents({
17486          
17487         /**
17488          * @event select
17489          * Fires when a date is selected
17490              * @param {Roo.form.DateField} combo This combo box
17491              * @param {Date} date The date selected
17492              */
17493         'select' : true
17494          
17495     });
17496     
17497     
17498     if(typeof this.minValue == "string") {
17499         this.minValue = this.parseDate(this.minValue);
17500     }
17501     if(typeof this.maxValue == "string") {
17502         this.maxValue = this.parseDate(this.maxValue);
17503     }
17504     this.ddMatch = null;
17505     if(this.disabledDates){
17506         var dd = this.disabledDates;
17507         var re = "(?:";
17508         for(var i = 0; i < dd.length; i++){
17509             re += dd[i];
17510             if(i != dd.length-1) {
17511                 re += "|";
17512             }
17513         }
17514         this.ddMatch = new RegExp(re + ")");
17515     }
17516 };
17517
17518 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17519     /**
17520      * @cfg {String} format
17521      * The default date format string which can be overriden for localization support.  The format must be
17522      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17523      */
17524     format : "m/d/y",
17525     /**
17526      * @cfg {String} altFormats
17527      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17528      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17529      */
17530     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17531     /**
17532      * @cfg {Array} disabledDays
17533      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17534      */
17535     disabledDays : null,
17536     /**
17537      * @cfg {String} disabledDaysText
17538      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17539      */
17540     disabledDaysText : "Disabled",
17541     /**
17542      * @cfg {Array} disabledDates
17543      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17544      * expression so they are very powerful. Some examples:
17545      * <ul>
17546      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17547      * <li>["03/08", "09/16"] would disable those days for every year</li>
17548      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17549      * <li>["03/../2006"] would disable every day in March 2006</li>
17550      * <li>["^03"] would disable every day in every March</li>
17551      * </ul>
17552      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17553      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17554      */
17555     disabledDates : null,
17556     /**
17557      * @cfg {String} disabledDatesText
17558      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17559      */
17560     disabledDatesText : "Disabled",
17561     /**
17562      * @cfg {Date/String} minValue
17563      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17564      * valid format (defaults to null).
17565      */
17566     minValue : null,
17567     /**
17568      * @cfg {Date/String} maxValue
17569      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17570      * valid format (defaults to null).
17571      */
17572     maxValue : null,
17573     /**
17574      * @cfg {String} minText
17575      * The error text to display when the date in the cell is before minValue (defaults to
17576      * 'The date in this field must be after {minValue}').
17577      */
17578     minText : "The date in this field must be equal to or after {0}",
17579     /**
17580      * @cfg {String} maxText
17581      * The error text to display when the date in the cell is after maxValue (defaults to
17582      * 'The date in this field must be before {maxValue}').
17583      */
17584     maxText : "The date in this field must be equal to or before {0}",
17585     /**
17586      * @cfg {String} invalidText
17587      * The error text to display when the date in the field is invalid (defaults to
17588      * '{value} is not a valid date - it must be in the format {format}').
17589      */
17590     invalidText : "{0} is not a valid date - it must be in the format {1}",
17591     /**
17592      * @cfg {String} triggerClass
17593      * An additional CSS class used to style the trigger button.  The trigger will always get the
17594      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17595      * which displays a calendar icon).
17596      */
17597     triggerClass : 'x-form-date-trigger',
17598     
17599
17600     /**
17601      * @cfg {Boolean} useIso
17602      * if enabled, then the date field will use a hidden field to store the 
17603      * real value as iso formated date. default (false)
17604      */ 
17605     useIso : false,
17606     /**
17607      * @cfg {String/Object} autoCreate
17608      * A DomHelper element spec, or true for a default element spec (defaults to
17609      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17610      */ 
17611     // private
17612     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17613     
17614     // private
17615     hiddenField: false,
17616     
17617     onRender : function(ct, position)
17618     {
17619         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17620         if (this.useIso) {
17621             //this.el.dom.removeAttribute('name'); 
17622             Roo.log("Changing name?");
17623             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17624             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17625                     'before', true);
17626             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17627             // prevent input submission
17628             this.hiddenName = this.name;
17629         }
17630             
17631             
17632     },
17633     
17634     // private
17635     validateValue : function(value)
17636     {
17637         value = this.formatDate(value);
17638         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17639             Roo.log('super failed');
17640             return false;
17641         }
17642         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17643              return true;
17644         }
17645         var svalue = value;
17646         value = this.parseDate(value);
17647         if(!value){
17648             Roo.log('parse date failed' + svalue);
17649             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17650             return false;
17651         }
17652         var time = value.getTime();
17653         if(this.minValue && time < this.minValue.getTime()){
17654             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17655             return false;
17656         }
17657         if(this.maxValue && time > this.maxValue.getTime()){
17658             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17659             return false;
17660         }
17661         if(this.disabledDays){
17662             var day = value.getDay();
17663             for(var i = 0; i < this.disabledDays.length; i++) {
17664                 if(day === this.disabledDays[i]){
17665                     this.markInvalid(this.disabledDaysText);
17666                     return false;
17667                 }
17668             }
17669         }
17670         var fvalue = this.formatDate(value);
17671         if(this.ddMatch && this.ddMatch.test(fvalue)){
17672             this.markInvalid(String.format(this.disabledDatesText, fvalue));
17673             return false;
17674         }
17675         return true;
17676     },
17677
17678     // private
17679     // Provides logic to override the default TriggerField.validateBlur which just returns true
17680     validateBlur : function(){
17681         return !this.menu || !this.menu.isVisible();
17682     },
17683     
17684     getName: function()
17685     {
17686         // returns hidden if it's set..
17687         if (!this.rendered) {return ''};
17688         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
17689         
17690     },
17691
17692     /**
17693      * Returns the current date value of the date field.
17694      * @return {Date} The date value
17695      */
17696     getValue : function(){
17697         
17698         return  this.hiddenField ?
17699                 this.hiddenField.value :
17700                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17701     },
17702
17703     /**
17704      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
17705      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17706      * (the default format used is "m/d/y").
17707      * <br />Usage:
17708      * <pre><code>
17709 //All of these calls set the same date value (May 4, 2006)
17710
17711 //Pass a date object:
17712 var dt = new Date('5/4/06');
17713 dateField.setValue(dt);
17714
17715 //Pass a date string (default format):
17716 dateField.setValue('5/4/06');
17717
17718 //Pass a date string (custom format):
17719 dateField.format = 'Y-m-d';
17720 dateField.setValue('2006-5-4');
17721 </code></pre>
17722      * @param {String/Date} date The date or valid date string
17723      */
17724     setValue : function(date){
17725         if (this.hiddenField) {
17726             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
17727         }
17728         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
17729         // make sure the value field is always stored as a date..
17730         this.value = this.parseDate(date);
17731         
17732         
17733     },
17734
17735     // private
17736     parseDate : function(value){
17737         if(!value || value instanceof Date){
17738             return value;
17739         }
17740         var v = Date.parseDate(value, this.format);
17741          if (!v && this.useIso) {
17742             v = Date.parseDate(value, 'Y-m-d');
17743         }
17744         if(!v && this.altFormats){
17745             if(!this.altFormatsArray){
17746                 this.altFormatsArray = this.altFormats.split("|");
17747             }
17748             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17749                 v = Date.parseDate(value, this.altFormatsArray[i]);
17750             }
17751         }
17752         return v;
17753     },
17754
17755     // private
17756     formatDate : function(date, fmt){
17757         return (!date || !(date instanceof Date)) ?
17758                date : date.dateFormat(fmt || this.format);
17759     },
17760
17761     // private
17762     menuListeners : {
17763         select: function(m, d){
17764             
17765             this.setValue(d);
17766             this.fireEvent('select', this, d);
17767         },
17768         show : function(){ // retain focus styling
17769             this.onFocus();
17770         },
17771         hide : function(){
17772             this.focus.defer(10, this);
17773             var ml = this.menuListeners;
17774             this.menu.un("select", ml.select,  this);
17775             this.menu.un("show", ml.show,  this);
17776             this.menu.un("hide", ml.hide,  this);
17777         }
17778     },
17779
17780     // private
17781     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
17782     onTriggerClick : function(){
17783         if(this.disabled){
17784             return;
17785         }
17786         if(this.menu == null){
17787             this.menu = new Roo.menu.DateMenu();
17788         }
17789         Roo.apply(this.menu.picker,  {
17790             showClear: this.allowBlank,
17791             minDate : this.minValue,
17792             maxDate : this.maxValue,
17793             disabledDatesRE : this.ddMatch,
17794             disabledDatesText : this.disabledDatesText,
17795             disabledDays : this.disabledDays,
17796             disabledDaysText : this.disabledDaysText,
17797             format : this.useIso ? 'Y-m-d' : this.format,
17798             minText : String.format(this.minText, this.formatDate(this.minValue)),
17799             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
17800         });
17801         this.menu.on(Roo.apply({}, this.menuListeners, {
17802             scope:this
17803         }));
17804         this.menu.picker.setValue(this.getValue() || new Date());
17805         this.menu.show(this.el, "tl-bl?");
17806     },
17807
17808     beforeBlur : function(){
17809         var v = this.parseDate(this.getRawValue());
17810         if(v){
17811             this.setValue(v);
17812         }
17813     },
17814
17815     /*@
17816      * overide
17817      * 
17818      */
17819     isDirty : function() {
17820         if(this.disabled) {
17821             return false;
17822         }
17823         
17824         if(typeof(this.startValue) === 'undefined'){
17825             return false;
17826         }
17827         
17828         return String(this.getValue()) !== String(this.startValue);
17829         
17830     },
17831     // @overide
17832     cleanLeadingSpace : function(e)
17833     {
17834        return;
17835     }
17836     
17837 });/*
17838  * Based on:
17839  * Ext JS Library 1.1.1
17840  * Copyright(c) 2006-2007, Ext JS, LLC.
17841  *
17842  * Originally Released Under LGPL - original licence link has changed is not relivant.
17843  *
17844  * Fork - LGPL
17845  * <script type="text/javascript">
17846  */
17847  
17848 /**
17849  * @class Roo.form.MonthField
17850  * @extends Roo.form.TriggerField
17851  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17852 * @constructor
17853 * Create a new MonthField
17854 * @param {Object} config
17855  */
17856 Roo.form.MonthField = function(config){
17857     
17858     Roo.form.MonthField.superclass.constructor.call(this, config);
17859     
17860       this.addEvents({
17861          
17862         /**
17863          * @event select
17864          * Fires when a date is selected
17865              * @param {Roo.form.MonthFieeld} combo This combo box
17866              * @param {Date} date The date selected
17867              */
17868         'select' : true
17869          
17870     });
17871     
17872     
17873     if(typeof this.minValue == "string") {
17874         this.minValue = this.parseDate(this.minValue);
17875     }
17876     if(typeof this.maxValue == "string") {
17877         this.maxValue = this.parseDate(this.maxValue);
17878     }
17879     this.ddMatch = null;
17880     if(this.disabledDates){
17881         var dd = this.disabledDates;
17882         var re = "(?:";
17883         for(var i = 0; i < dd.length; i++){
17884             re += dd[i];
17885             if(i != dd.length-1) {
17886                 re += "|";
17887             }
17888         }
17889         this.ddMatch = new RegExp(re + ")");
17890     }
17891 };
17892
17893 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
17894     /**
17895      * @cfg {String} format
17896      * The default date format string which can be overriden for localization support.  The format must be
17897      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17898      */
17899     format : "M Y",
17900     /**
17901      * @cfg {String} altFormats
17902      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17903      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17904      */
17905     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
17906     /**
17907      * @cfg {Array} disabledDays
17908      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17909      */
17910     disabledDays : [0,1,2,3,4,5,6],
17911     /**
17912      * @cfg {String} disabledDaysText
17913      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17914      */
17915     disabledDaysText : "Disabled",
17916     /**
17917      * @cfg {Array} disabledDates
17918      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17919      * expression so they are very powerful. Some examples:
17920      * <ul>
17921      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17922      * <li>["03/08", "09/16"] would disable those days for every year</li>
17923      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17924      * <li>["03/../2006"] would disable every day in March 2006</li>
17925      * <li>["^03"] would disable every day in every March</li>
17926      * </ul>
17927      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17928      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17929      */
17930     disabledDates : null,
17931     /**
17932      * @cfg {String} disabledDatesText
17933      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17934      */
17935     disabledDatesText : "Disabled",
17936     /**
17937      * @cfg {Date/String} minValue
17938      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17939      * valid format (defaults to null).
17940      */
17941     minValue : null,
17942     /**
17943      * @cfg {Date/String} maxValue
17944      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17945      * valid format (defaults to null).
17946      */
17947     maxValue : null,
17948     /**
17949      * @cfg {String} minText
17950      * The error text to display when the date in the cell is before minValue (defaults to
17951      * 'The date in this field must be after {minValue}').
17952      */
17953     minText : "The date in this field must be equal to or after {0}",
17954     /**
17955      * @cfg {String} maxTextf
17956      * The error text to display when the date in the cell is after maxValue (defaults to
17957      * 'The date in this field must be before {maxValue}').
17958      */
17959     maxText : "The date in this field must be equal to or before {0}",
17960     /**
17961      * @cfg {String} invalidText
17962      * The error text to display when the date in the field is invalid (defaults to
17963      * '{value} is not a valid date - it must be in the format {format}').
17964      */
17965     invalidText : "{0} is not a valid date - it must be in the format {1}",
17966     /**
17967      * @cfg {String} triggerClass
17968      * An additional CSS class used to style the trigger button.  The trigger will always get the
17969      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17970      * which displays a calendar icon).
17971      */
17972     triggerClass : 'x-form-date-trigger',
17973     
17974
17975     /**
17976      * @cfg {Boolean} useIso
17977      * if enabled, then the date field will use a hidden field to store the 
17978      * real value as iso formated date. default (true)
17979      */ 
17980     useIso : true,
17981     /**
17982      * @cfg {String/Object} autoCreate
17983      * A DomHelper element spec, or true for a default element spec (defaults to
17984      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17985      */ 
17986     // private
17987     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
17988     
17989     // private
17990     hiddenField: false,
17991     
17992     hideMonthPicker : false,
17993     
17994     onRender : function(ct, position)
17995     {
17996         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
17997         if (this.useIso) {
17998             this.el.dom.removeAttribute('name'); 
17999             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18000                     'before', true);
18001             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18002             // prevent input submission
18003             this.hiddenName = this.name;
18004         }
18005             
18006             
18007     },
18008     
18009     // private
18010     validateValue : function(value)
18011     {
18012         value = this.formatDate(value);
18013         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18014             return false;
18015         }
18016         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18017              return true;
18018         }
18019         var svalue = value;
18020         value = this.parseDate(value);
18021         if(!value){
18022             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18023             return false;
18024         }
18025         var time = value.getTime();
18026         if(this.minValue && time < this.minValue.getTime()){
18027             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18028             return false;
18029         }
18030         if(this.maxValue && time > this.maxValue.getTime()){
18031             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18032             return false;
18033         }
18034         /*if(this.disabledDays){
18035             var day = value.getDay();
18036             for(var i = 0; i < this.disabledDays.length; i++) {
18037                 if(day === this.disabledDays[i]){
18038                     this.markInvalid(this.disabledDaysText);
18039                     return false;
18040                 }
18041             }
18042         }
18043         */
18044         var fvalue = this.formatDate(value);
18045         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18046             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18047             return false;
18048         }
18049         */
18050         return true;
18051     },
18052
18053     // private
18054     // Provides logic to override the default TriggerField.validateBlur which just returns true
18055     validateBlur : function(){
18056         return !this.menu || !this.menu.isVisible();
18057     },
18058
18059     /**
18060      * Returns the current date value of the date field.
18061      * @return {Date} The date value
18062      */
18063     getValue : function(){
18064         
18065         
18066         
18067         return  this.hiddenField ?
18068                 this.hiddenField.value :
18069                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18070     },
18071
18072     /**
18073      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18074      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18075      * (the default format used is "m/d/y").
18076      * <br />Usage:
18077      * <pre><code>
18078 //All of these calls set the same date value (May 4, 2006)
18079
18080 //Pass a date object:
18081 var dt = new Date('5/4/06');
18082 monthField.setValue(dt);
18083
18084 //Pass a date string (default format):
18085 monthField.setValue('5/4/06');
18086
18087 //Pass a date string (custom format):
18088 monthField.format = 'Y-m-d';
18089 monthField.setValue('2006-5-4');
18090 </code></pre>
18091      * @param {String/Date} date The date or valid date string
18092      */
18093     setValue : function(date){
18094         Roo.log('month setValue' + date);
18095         // can only be first of month..
18096         
18097         var val = this.parseDate(date);
18098         
18099         if (this.hiddenField) {
18100             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18101         }
18102         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18103         this.value = this.parseDate(date);
18104     },
18105
18106     // private
18107     parseDate : function(value){
18108         if(!value || value instanceof Date){
18109             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18110             return value;
18111         }
18112         var v = Date.parseDate(value, this.format);
18113         if (!v && this.useIso) {
18114             v = Date.parseDate(value, 'Y-m-d');
18115         }
18116         if (v) {
18117             // 
18118             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18119         }
18120         
18121         
18122         if(!v && this.altFormats){
18123             if(!this.altFormatsArray){
18124                 this.altFormatsArray = this.altFormats.split("|");
18125             }
18126             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18127                 v = Date.parseDate(value, this.altFormatsArray[i]);
18128             }
18129         }
18130         return v;
18131     },
18132
18133     // private
18134     formatDate : function(date, fmt){
18135         return (!date || !(date instanceof Date)) ?
18136                date : date.dateFormat(fmt || this.format);
18137     },
18138
18139     // private
18140     menuListeners : {
18141         select: function(m, d){
18142             this.setValue(d);
18143             this.fireEvent('select', this, d);
18144         },
18145         show : function(){ // retain focus styling
18146             this.onFocus();
18147         },
18148         hide : function(){
18149             this.focus.defer(10, this);
18150             var ml = this.menuListeners;
18151             this.menu.un("select", ml.select,  this);
18152             this.menu.un("show", ml.show,  this);
18153             this.menu.un("hide", ml.hide,  this);
18154         }
18155     },
18156     // private
18157     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18158     onTriggerClick : function(){
18159         if(this.disabled){
18160             return;
18161         }
18162         if(this.menu == null){
18163             this.menu = new Roo.menu.DateMenu();
18164            
18165         }
18166         
18167         Roo.apply(this.menu.picker,  {
18168             
18169             showClear: this.allowBlank,
18170             minDate : this.minValue,
18171             maxDate : this.maxValue,
18172             disabledDatesRE : this.ddMatch,
18173             disabledDatesText : this.disabledDatesText,
18174             
18175             format : this.useIso ? 'Y-m-d' : this.format,
18176             minText : String.format(this.minText, this.formatDate(this.minValue)),
18177             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18178             
18179         });
18180          this.menu.on(Roo.apply({}, this.menuListeners, {
18181             scope:this
18182         }));
18183        
18184         
18185         var m = this.menu;
18186         var p = m.picker;
18187         
18188         // hide month picker get's called when we called by 'before hide';
18189         
18190         var ignorehide = true;
18191         p.hideMonthPicker  = function(disableAnim){
18192             if (ignorehide) {
18193                 return;
18194             }
18195              if(this.monthPicker){
18196                 Roo.log("hideMonthPicker called");
18197                 if(disableAnim === true){
18198                     this.monthPicker.hide();
18199                 }else{
18200                     this.monthPicker.slideOut('t', {duration:.2});
18201                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18202                     p.fireEvent("select", this, this.value);
18203                     m.hide();
18204                 }
18205             }
18206         }
18207         
18208         Roo.log('picker set value');
18209         Roo.log(this.getValue());
18210         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18211         m.show(this.el, 'tl-bl?');
18212         ignorehide  = false;
18213         // this will trigger hideMonthPicker..
18214         
18215         
18216         // hidden the day picker
18217         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18218         
18219         
18220         
18221       
18222         
18223         p.showMonthPicker.defer(100, p);
18224     
18225         
18226        
18227     },
18228
18229     beforeBlur : function(){
18230         var v = this.parseDate(this.getRawValue());
18231         if(v){
18232             this.setValue(v);
18233         }
18234     }
18235
18236     /** @cfg {Boolean} grow @hide */
18237     /** @cfg {Number} growMin @hide */
18238     /** @cfg {Number} growMax @hide */
18239     /**
18240      * @hide
18241      * @method autoSize
18242      */
18243 });/*
18244  * Based on:
18245  * Ext JS Library 1.1.1
18246  * Copyright(c) 2006-2007, Ext JS, LLC.
18247  *
18248  * Originally Released Under LGPL - original licence link has changed is not relivant.
18249  *
18250  * Fork - LGPL
18251  * <script type="text/javascript">
18252  */
18253  
18254
18255 /**
18256  * @class Roo.form.ComboBox
18257  * @extends Roo.form.TriggerField
18258  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18259  * @constructor
18260  * Create a new ComboBox.
18261  * @param {Object} config Configuration options
18262  */
18263 Roo.form.ComboBox = function(config){
18264     Roo.form.ComboBox.superclass.constructor.call(this, config);
18265     this.addEvents({
18266         /**
18267          * @event expand
18268          * Fires when the dropdown list is expanded
18269              * @param {Roo.form.ComboBox} combo This combo box
18270              */
18271         'expand' : true,
18272         /**
18273          * @event collapse
18274          * Fires when the dropdown list is collapsed
18275              * @param {Roo.form.ComboBox} combo This combo box
18276              */
18277         'collapse' : true,
18278         /**
18279          * @event beforeselect
18280          * Fires before a list item is selected. Return false to cancel the selection.
18281              * @param {Roo.form.ComboBox} combo This combo box
18282              * @param {Roo.data.Record} record The data record returned from the underlying store
18283              * @param {Number} index The index of the selected item in the dropdown list
18284              */
18285         'beforeselect' : true,
18286         /**
18287          * @event select
18288          * Fires when a list item is selected
18289              * @param {Roo.form.ComboBox} combo This combo box
18290              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18291              * @param {Number} index The index of the selected item in the dropdown list
18292              */
18293         'select' : true,
18294         /**
18295          * @event beforequery
18296          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18297          * The event object passed has these properties:
18298              * @param {Roo.form.ComboBox} combo This combo box
18299              * @param {String} query The query
18300              * @param {Boolean} forceAll true to force "all" query
18301              * @param {Boolean} cancel true to cancel the query
18302              * @param {Object} e The query event object
18303              */
18304         'beforequery': true,
18305          /**
18306          * @event add
18307          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18308              * @param {Roo.form.ComboBox} combo This combo box
18309              */
18310         'add' : true,
18311         /**
18312          * @event edit
18313          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18314              * @param {Roo.form.ComboBox} combo This combo box
18315              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18316              */
18317         'edit' : true
18318         
18319         
18320     });
18321     if(this.transform){
18322         this.allowDomMove = false;
18323         var s = Roo.getDom(this.transform);
18324         if(!this.hiddenName){
18325             this.hiddenName = s.name;
18326         }
18327         if(!this.store){
18328             this.mode = 'local';
18329             var d = [], opts = s.options;
18330             for(var i = 0, len = opts.length;i < len; i++){
18331                 var o = opts[i];
18332                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18333                 if(o.selected) {
18334                     this.value = value;
18335                 }
18336                 d.push([value, o.text]);
18337             }
18338             this.store = new Roo.data.SimpleStore({
18339                 'id': 0,
18340                 fields: ['value', 'text'],
18341                 data : d
18342             });
18343             this.valueField = 'value';
18344             this.displayField = 'text';
18345         }
18346         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18347         if(!this.lazyRender){
18348             this.target = true;
18349             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18350             s.parentNode.removeChild(s); // remove it
18351             this.render(this.el.parentNode);
18352         }else{
18353             s.parentNode.removeChild(s); // remove it
18354         }
18355
18356     }
18357     if (this.store) {
18358         this.store = Roo.factory(this.store, Roo.data);
18359     }
18360     
18361     this.selectedIndex = -1;
18362     if(this.mode == 'local'){
18363         if(config.queryDelay === undefined){
18364             this.queryDelay = 10;
18365         }
18366         if(config.minChars === undefined){
18367             this.minChars = 0;
18368         }
18369     }
18370 };
18371
18372 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18373     /**
18374      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18375      */
18376     /**
18377      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18378      * rendering into an Roo.Editor, defaults to false)
18379      */
18380     /**
18381      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18382      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18383      */
18384     /**
18385      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18386      */
18387     /**
18388      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18389      * the dropdown list (defaults to undefined, with no header element)
18390      */
18391
18392      /**
18393      * @cfg {String/Roo.Template} tpl The template to use to render the output
18394      */
18395      
18396     // private
18397     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18398     /**
18399      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18400      */
18401     listWidth: undefined,
18402     /**
18403      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18404      * mode = 'remote' or 'text' if mode = 'local')
18405      */
18406     displayField: undefined,
18407     /**
18408      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18409      * mode = 'remote' or 'value' if mode = 'local'). 
18410      * Note: use of a valueField requires the user make a selection
18411      * in order for a value to be mapped.
18412      */
18413     valueField: undefined,
18414     
18415     
18416     /**
18417      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18418      * field's data value (defaults to the underlying DOM element's name)
18419      */
18420     hiddenName: undefined,
18421     /**
18422      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18423      */
18424     listClass: '',
18425     /**
18426      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18427      */
18428     selectedClass: 'x-combo-selected',
18429     /**
18430      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18431      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18432      * which displays a downward arrow icon).
18433      */
18434     triggerClass : 'x-form-arrow-trigger',
18435     /**
18436      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18437      */
18438     shadow:'sides',
18439     /**
18440      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18441      * anchor positions (defaults to 'tl-bl')
18442      */
18443     listAlign: 'tl-bl?',
18444     /**
18445      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18446      */
18447     maxHeight: 300,
18448     /**
18449      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18450      * query specified by the allQuery config option (defaults to 'query')
18451      */
18452     triggerAction: 'query',
18453     /**
18454      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18455      * (defaults to 4, does not apply if editable = false)
18456      */
18457     minChars : 4,
18458     /**
18459      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18460      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18461      */
18462     typeAhead: false,
18463     /**
18464      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18465      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18466      */
18467     queryDelay: 500,
18468     /**
18469      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18470      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18471      */
18472     pageSize: 0,
18473     /**
18474      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18475      * when editable = true (defaults to false)
18476      */
18477     selectOnFocus:false,
18478     /**
18479      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18480      */
18481     queryParam: 'query',
18482     /**
18483      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18484      * when mode = 'remote' (defaults to 'Loading...')
18485      */
18486     loadingText: 'Loading...',
18487     /**
18488      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18489      */
18490     resizable: false,
18491     /**
18492      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18493      */
18494     handleHeight : 8,
18495     /**
18496      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18497      * traditional select (defaults to true)
18498      */
18499     editable: true,
18500     /**
18501      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18502      */
18503     allQuery: '',
18504     /**
18505      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18506      */
18507     mode: 'remote',
18508     /**
18509      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18510      * listWidth has a higher value)
18511      */
18512     minListWidth : 70,
18513     /**
18514      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18515      * allow the user to set arbitrary text into the field (defaults to false)
18516      */
18517     forceSelection:false,
18518     /**
18519      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18520      * if typeAhead = true (defaults to 250)
18521      */
18522     typeAheadDelay : 250,
18523     /**
18524      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18525      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18526      */
18527     valueNotFoundText : undefined,
18528     /**
18529      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18530      */
18531     blockFocus : false,
18532     
18533     /**
18534      * @cfg {Boolean} disableClear Disable showing of clear button.
18535      */
18536     disableClear : false,
18537     /**
18538      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18539      */
18540     alwaysQuery : false,
18541     
18542     //private
18543     addicon : false,
18544     editicon: false,
18545     
18546     // element that contains real text value.. (when hidden is used..)
18547      
18548     // private
18549     onRender : function(ct, position)
18550     {
18551         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18552         
18553         if(this.hiddenName){
18554             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18555                     'before', true);
18556             this.hiddenField.value =
18557                 this.hiddenValue !== undefined ? this.hiddenValue :
18558                 this.value !== undefined ? this.value : '';
18559
18560             // prevent input submission
18561             this.el.dom.removeAttribute('name');
18562              
18563              
18564         }
18565         
18566         if(Roo.isGecko){
18567             this.el.dom.setAttribute('autocomplete', 'off');
18568         }
18569
18570         var cls = 'x-combo-list';
18571
18572         this.list = new Roo.Layer({
18573             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18574         });
18575
18576         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18577         this.list.setWidth(lw);
18578         this.list.swallowEvent('mousewheel');
18579         this.assetHeight = 0;
18580
18581         if(this.title){
18582             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18583             this.assetHeight += this.header.getHeight();
18584         }
18585
18586         this.innerList = this.list.createChild({cls:cls+'-inner'});
18587         this.innerList.on('mouseover', this.onViewOver, this);
18588         this.innerList.on('mousemove', this.onViewMove, this);
18589         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18590         
18591         if(this.allowBlank && !this.pageSize && !this.disableClear){
18592             this.footer = this.list.createChild({cls:cls+'-ft'});
18593             this.pageTb = new Roo.Toolbar(this.footer);
18594            
18595         }
18596         if(this.pageSize){
18597             this.footer = this.list.createChild({cls:cls+'-ft'});
18598             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18599                     {pageSize: this.pageSize});
18600             
18601         }
18602         
18603         if (this.pageTb && this.allowBlank && !this.disableClear) {
18604             var _this = this;
18605             this.pageTb.add(new Roo.Toolbar.Fill(), {
18606                 cls: 'x-btn-icon x-btn-clear',
18607                 text: '&#160;',
18608                 handler: function()
18609                 {
18610                     _this.collapse();
18611                     _this.clearValue();
18612                     _this.onSelect(false, -1);
18613                 }
18614             });
18615         }
18616         if (this.footer) {
18617             this.assetHeight += this.footer.getHeight();
18618         }
18619         
18620
18621         if(!this.tpl){
18622             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18623         }
18624
18625         this.view = new Roo.View(this.innerList, this.tpl, {
18626             singleSelect:true,
18627             store: this.store,
18628             selectedClass: this.selectedClass
18629         });
18630
18631         this.view.on('click', this.onViewClick, this);
18632
18633         this.store.on('beforeload', this.onBeforeLoad, this);
18634         this.store.on('load', this.onLoad, this);
18635         this.store.on('loadexception', this.onLoadException, this);
18636
18637         if(this.resizable){
18638             this.resizer = new Roo.Resizable(this.list,  {
18639                pinned:true, handles:'se'
18640             });
18641             this.resizer.on('resize', function(r, w, h){
18642                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18643                 this.listWidth = w;
18644                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18645                 this.restrictHeight();
18646             }, this);
18647             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18648         }
18649         if(!this.editable){
18650             this.editable = true;
18651             this.setEditable(false);
18652         }  
18653         
18654         
18655         if (typeof(this.events.add.listeners) != 'undefined') {
18656             
18657             this.addicon = this.wrap.createChild(
18658                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
18659        
18660             this.addicon.on('click', function(e) {
18661                 this.fireEvent('add', this);
18662             }, this);
18663         }
18664         if (typeof(this.events.edit.listeners) != 'undefined') {
18665             
18666             this.editicon = this.wrap.createChild(
18667                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
18668             if (this.addicon) {
18669                 this.editicon.setStyle('margin-left', '40px');
18670             }
18671             this.editicon.on('click', function(e) {
18672                 
18673                 // we fire even  if inothing is selected..
18674                 this.fireEvent('edit', this, this.lastData );
18675                 
18676             }, this);
18677         }
18678         
18679         
18680         
18681     },
18682
18683     // private
18684     initEvents : function(){
18685         Roo.form.ComboBox.superclass.initEvents.call(this);
18686
18687         this.keyNav = new Roo.KeyNav(this.el, {
18688             "up" : function(e){
18689                 this.inKeyMode = true;
18690                 this.selectPrev();
18691             },
18692
18693             "down" : function(e){
18694                 if(!this.isExpanded()){
18695                     this.onTriggerClick();
18696                 }else{
18697                     this.inKeyMode = true;
18698                     this.selectNext();
18699                 }
18700             },
18701
18702             "enter" : function(e){
18703                 this.onViewClick();
18704                 //return true;
18705             },
18706
18707             "esc" : function(e){
18708                 this.collapse();
18709             },
18710
18711             "tab" : function(e){
18712                 this.onViewClick(false);
18713                 this.fireEvent("specialkey", this, e);
18714                 return true;
18715             },
18716
18717             scope : this,
18718
18719             doRelay : function(foo, bar, hname){
18720                 if(hname == 'down' || this.scope.isExpanded()){
18721                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18722                 }
18723                 return true;
18724             },
18725
18726             forceKeyDown: true
18727         });
18728         this.queryDelay = Math.max(this.queryDelay || 10,
18729                 this.mode == 'local' ? 10 : 250);
18730         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18731         if(this.typeAhead){
18732             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18733         }
18734         if(this.editable !== false){
18735             this.el.on("keyup", this.onKeyUp, this);
18736         }
18737         if(this.forceSelection){
18738             this.on('blur', this.doForce, this);
18739         }
18740     },
18741
18742     onDestroy : function(){
18743         if(this.view){
18744             this.view.setStore(null);
18745             this.view.el.removeAllListeners();
18746             this.view.el.remove();
18747             this.view.purgeListeners();
18748         }
18749         if(this.list){
18750             this.list.destroy();
18751         }
18752         if(this.store){
18753             this.store.un('beforeload', this.onBeforeLoad, this);
18754             this.store.un('load', this.onLoad, this);
18755             this.store.un('loadexception', this.onLoadException, this);
18756         }
18757         Roo.form.ComboBox.superclass.onDestroy.call(this);
18758     },
18759
18760     // private
18761     fireKey : function(e){
18762         if(e.isNavKeyPress() && !this.list.isVisible()){
18763             this.fireEvent("specialkey", this, e);
18764         }
18765     },
18766
18767     // private
18768     onResize: function(w, h){
18769         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
18770         
18771         if(typeof w != 'number'){
18772             // we do not handle it!?!?
18773             return;
18774         }
18775         var tw = this.trigger.getWidth();
18776         tw += this.addicon ? this.addicon.getWidth() : 0;
18777         tw += this.editicon ? this.editicon.getWidth() : 0;
18778         var x = w - tw;
18779         this.el.setWidth( this.adjustWidth('input', x));
18780             
18781         this.trigger.setStyle('left', x+'px');
18782         
18783         if(this.list && this.listWidth === undefined){
18784             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
18785             this.list.setWidth(lw);
18786             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18787         }
18788         
18789     
18790         
18791     },
18792
18793     /**
18794      * Allow or prevent the user from directly editing the field text.  If false is passed,
18795      * the user will only be able to select from the items defined in the dropdown list.  This method
18796      * is the runtime equivalent of setting the 'editable' config option at config time.
18797      * @param {Boolean} value True to allow the user to directly edit the field text
18798      */
18799     setEditable : function(value){
18800         if(value == this.editable){
18801             return;
18802         }
18803         this.editable = value;
18804         if(!value){
18805             this.el.dom.setAttribute('readOnly', true);
18806             this.el.on('mousedown', this.onTriggerClick,  this);
18807             this.el.addClass('x-combo-noedit');
18808         }else{
18809             this.el.dom.setAttribute('readOnly', false);
18810             this.el.un('mousedown', this.onTriggerClick,  this);
18811             this.el.removeClass('x-combo-noedit');
18812         }
18813     },
18814
18815     // private
18816     onBeforeLoad : function(){
18817         if(!this.hasFocus){
18818             return;
18819         }
18820         this.innerList.update(this.loadingText ?
18821                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
18822         this.restrictHeight();
18823         this.selectedIndex = -1;
18824     },
18825
18826     // private
18827     onLoad : function(){
18828         if(!this.hasFocus){
18829             return;
18830         }
18831         if(this.store.getCount() > 0){
18832             this.expand();
18833             this.restrictHeight();
18834             if(this.lastQuery == this.allQuery){
18835                 if(this.editable){
18836                     this.el.dom.select();
18837                 }
18838                 if(!this.selectByValue(this.value, true)){
18839                     this.select(0, true);
18840                 }
18841             }else{
18842                 this.selectNext();
18843                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18844                     this.taTask.delay(this.typeAheadDelay);
18845                 }
18846             }
18847         }else{
18848             this.onEmptyResults();
18849         }
18850         //this.el.focus();
18851     },
18852     // private
18853     onLoadException : function()
18854     {
18855         this.collapse();
18856         Roo.log(this.store.reader.jsonData);
18857         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18858             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18859         }
18860         
18861         
18862     },
18863     // private
18864     onTypeAhead : function(){
18865         if(this.store.getCount() > 0){
18866             var r = this.store.getAt(0);
18867             var newValue = r.data[this.displayField];
18868             var len = newValue.length;
18869             var selStart = this.getRawValue().length;
18870             if(selStart != len){
18871                 this.setRawValue(newValue);
18872                 this.selectText(selStart, newValue.length);
18873             }
18874         }
18875     },
18876
18877     // private
18878     onSelect : function(record, index){
18879         if(this.fireEvent('beforeselect', this, record, index) !== false){
18880             this.setFromData(index > -1 ? record.data : false);
18881             this.collapse();
18882             this.fireEvent('select', this, record, index);
18883         }
18884     },
18885
18886     /**
18887      * Returns the currently selected field value or empty string if no value is set.
18888      * @return {String} value The selected value
18889      */
18890     getValue : function(){
18891         if(this.valueField){
18892             return typeof this.value != 'undefined' ? this.value : '';
18893         }
18894         return Roo.form.ComboBox.superclass.getValue.call(this);
18895     },
18896
18897     /**
18898      * Clears any text/value currently set in the field
18899      */
18900     clearValue : function(){
18901         if(this.hiddenField){
18902             this.hiddenField.value = '';
18903         }
18904         this.value = '';
18905         this.setRawValue('');
18906         this.lastSelectionText = '';
18907         
18908     },
18909
18910     /**
18911      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18912      * will be displayed in the field.  If the value does not match the data value of an existing item,
18913      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18914      * Otherwise the field will be blank (although the value will still be set).
18915      * @param {String} value The value to match
18916      */
18917     setValue : function(v){
18918         var text = v;
18919         if(this.valueField){
18920             var r = this.findRecord(this.valueField, v);
18921             if(r){
18922                 text = r.data[this.displayField];
18923             }else if(this.valueNotFoundText !== undefined){
18924                 text = this.valueNotFoundText;
18925             }
18926         }
18927         this.lastSelectionText = text;
18928         if(this.hiddenField){
18929             this.hiddenField.value = v;
18930         }
18931         Roo.form.ComboBox.superclass.setValue.call(this, text);
18932         this.value = v;
18933     },
18934     /**
18935      * @property {Object} the last set data for the element
18936      */
18937     
18938     lastData : false,
18939     /**
18940      * Sets the value of the field based on a object which is related to the record format for the store.
18941      * @param {Object} value the value to set as. or false on reset?
18942      */
18943     setFromData : function(o){
18944         var dv = ''; // display value
18945         var vv = ''; // value value..
18946         this.lastData = o;
18947         if (this.displayField) {
18948             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18949         } else {
18950             // this is an error condition!!!
18951             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18952         }
18953         
18954         if(this.valueField){
18955             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18956         }
18957         if(this.hiddenField){
18958             this.hiddenField.value = vv;
18959             
18960             this.lastSelectionText = dv;
18961             Roo.form.ComboBox.superclass.setValue.call(this, dv);
18962             this.value = vv;
18963             return;
18964         }
18965         // no hidden field.. - we store the value in 'value', but still display
18966         // display field!!!!
18967         this.lastSelectionText = dv;
18968         Roo.form.ComboBox.superclass.setValue.call(this, dv);
18969         this.value = vv;
18970         
18971         
18972     },
18973     // private
18974     reset : function(){
18975         // overridden so that last data is reset..
18976         this.setValue(this.resetValue);
18977         this.originalValue = this.getValue();
18978         this.clearInvalid();
18979         this.lastData = false;
18980         if (this.view) {
18981             this.view.clearSelections();
18982         }
18983     },
18984     // private
18985     findRecord : function(prop, value){
18986         var record;
18987         if(this.store.getCount() > 0){
18988             this.store.each(function(r){
18989                 if(r.data[prop] == value){
18990                     record = r;
18991                     return false;
18992                 }
18993                 return true;
18994             });
18995         }
18996         return record;
18997     },
18998     
18999     getName: function()
19000     {
19001         // returns hidden if it's set..
19002         if (!this.rendered) {return ''};
19003         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
19004         
19005     },
19006     // private
19007     onViewMove : function(e, t){
19008         this.inKeyMode = false;
19009     },
19010
19011     // private
19012     onViewOver : function(e, t){
19013         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19014             return;
19015         }
19016         var item = this.view.findItemFromChild(t);
19017         if(item){
19018             var index = this.view.indexOf(item);
19019             this.select(index, false);
19020         }
19021     },
19022
19023     // private
19024     onViewClick : function(doFocus)
19025     {
19026         var index = this.view.getSelectedIndexes()[0];
19027         var r = this.store.getAt(index);
19028         if(r){
19029             this.onSelect(r, index);
19030         }
19031         if(doFocus !== false && !this.blockFocus){
19032             this.el.focus();
19033         }
19034     },
19035
19036     // private
19037     restrictHeight : function(){
19038         this.innerList.dom.style.height = '';
19039         var inner = this.innerList.dom;
19040         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19041         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19042         this.list.beginUpdate();
19043         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19044         this.list.alignTo(this.el, this.listAlign);
19045         this.list.endUpdate();
19046     },
19047
19048     // private
19049     onEmptyResults : function(){
19050         this.collapse();
19051     },
19052
19053     /**
19054      * Returns true if the dropdown list is expanded, else false.
19055      */
19056     isExpanded : function(){
19057         return this.list.isVisible();
19058     },
19059
19060     /**
19061      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19062      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19063      * @param {String} value The data value of the item to select
19064      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19065      * selected item if it is not currently in view (defaults to true)
19066      * @return {Boolean} True if the value matched an item in the list, else false
19067      */
19068     selectByValue : function(v, scrollIntoView){
19069         if(v !== undefined && v !== null){
19070             var r = this.findRecord(this.valueField || this.displayField, v);
19071             if(r){
19072                 this.select(this.store.indexOf(r), scrollIntoView);
19073                 return true;
19074             }
19075         }
19076         return false;
19077     },
19078
19079     /**
19080      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19081      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19082      * @param {Number} index The zero-based index of the list item to select
19083      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19084      * selected item if it is not currently in view (defaults to true)
19085      */
19086     select : function(index, scrollIntoView){
19087         this.selectedIndex = index;
19088         this.view.select(index);
19089         if(scrollIntoView !== false){
19090             var el = this.view.getNode(index);
19091             if(el){
19092                 this.innerList.scrollChildIntoView(el, false);
19093             }
19094         }
19095     },
19096
19097     // private
19098     selectNext : function(){
19099         var ct = this.store.getCount();
19100         if(ct > 0){
19101             if(this.selectedIndex == -1){
19102                 this.select(0);
19103             }else if(this.selectedIndex < ct-1){
19104                 this.select(this.selectedIndex+1);
19105             }
19106         }
19107     },
19108
19109     // private
19110     selectPrev : function(){
19111         var ct = this.store.getCount();
19112         if(ct > 0){
19113             if(this.selectedIndex == -1){
19114                 this.select(0);
19115             }else if(this.selectedIndex != 0){
19116                 this.select(this.selectedIndex-1);
19117             }
19118         }
19119     },
19120
19121     // private
19122     onKeyUp : function(e){
19123         if(this.editable !== false && !e.isSpecialKey()){
19124             this.lastKey = e.getKey();
19125             this.dqTask.delay(this.queryDelay);
19126         }
19127     },
19128
19129     // private
19130     validateBlur : function(){
19131         return !this.list || !this.list.isVisible();   
19132     },
19133
19134     // private
19135     initQuery : function(){
19136         this.doQuery(this.getRawValue());
19137     },
19138
19139     // private
19140     doForce : function(){
19141         if(this.el.dom.value.length > 0){
19142             this.el.dom.value =
19143                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19144              
19145         }
19146     },
19147
19148     /**
19149      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19150      * query allowing the query action to be canceled if needed.
19151      * @param {String} query The SQL query to execute
19152      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19153      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19154      * saved in the current store (defaults to false)
19155      */
19156     doQuery : function(q, forceAll){
19157         if(q === undefined || q === null){
19158             q = '';
19159         }
19160         var qe = {
19161             query: q,
19162             forceAll: forceAll,
19163             combo: this,
19164             cancel:false
19165         };
19166         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19167             return false;
19168         }
19169         q = qe.query;
19170         forceAll = qe.forceAll;
19171         if(forceAll === true || (q.length >= this.minChars)){
19172             if(this.lastQuery != q || this.alwaysQuery){
19173                 this.lastQuery = q;
19174                 if(this.mode == 'local'){
19175                     this.selectedIndex = -1;
19176                     if(forceAll){
19177                         this.store.clearFilter();
19178                     }else{
19179                         this.store.filter(this.displayField, q);
19180                     }
19181                     this.onLoad();
19182                 }else{
19183                     this.store.baseParams[this.queryParam] = q;
19184                     this.store.load({
19185                         params: this.getParams(q)
19186                     });
19187                     this.expand();
19188                 }
19189             }else{
19190                 this.selectedIndex = -1;
19191                 this.onLoad();   
19192             }
19193         }
19194     },
19195
19196     // private
19197     getParams : function(q){
19198         var p = {};
19199         //p[this.queryParam] = q;
19200         if(this.pageSize){
19201             p.start = 0;
19202             p.limit = this.pageSize;
19203         }
19204         return p;
19205     },
19206
19207     /**
19208      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19209      */
19210     collapse : function(){
19211         if(!this.isExpanded()){
19212             return;
19213         }
19214         this.list.hide();
19215         Roo.get(document).un('mousedown', this.collapseIf, this);
19216         Roo.get(document).un('mousewheel', this.collapseIf, this);
19217         if (!this.editable) {
19218             Roo.get(document).un('keydown', this.listKeyPress, this);
19219         }
19220         this.fireEvent('collapse', this);
19221     },
19222
19223     // private
19224     collapseIf : function(e){
19225         if(!e.within(this.wrap) && !e.within(this.list)){
19226             this.collapse();
19227         }
19228     },
19229
19230     /**
19231      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19232      */
19233     expand : function(){
19234         if(this.isExpanded() || !this.hasFocus){
19235             return;
19236         }
19237         this.list.alignTo(this.el, this.listAlign);
19238         this.list.show();
19239         Roo.get(document).on('mousedown', this.collapseIf, this);
19240         Roo.get(document).on('mousewheel', this.collapseIf, this);
19241         if (!this.editable) {
19242             Roo.get(document).on('keydown', this.listKeyPress, this);
19243         }
19244         
19245         this.fireEvent('expand', this);
19246     },
19247
19248     // private
19249     // Implements the default empty TriggerField.onTriggerClick function
19250     onTriggerClick : function(){
19251         if(this.disabled){
19252             return;
19253         }
19254         if(this.isExpanded()){
19255             this.collapse();
19256             if (!this.blockFocus) {
19257                 this.el.focus();
19258             }
19259             
19260         }else {
19261             this.hasFocus = true;
19262             if(this.triggerAction == 'all') {
19263                 this.doQuery(this.allQuery, true);
19264             } else {
19265                 this.doQuery(this.getRawValue());
19266             }
19267             if (!this.blockFocus) {
19268                 this.el.focus();
19269             }
19270         }
19271     },
19272     listKeyPress : function(e)
19273     {
19274         //Roo.log('listkeypress');
19275         // scroll to first matching element based on key pres..
19276         if (e.isSpecialKey()) {
19277             return false;
19278         }
19279         var k = String.fromCharCode(e.getKey()).toUpperCase();
19280         //Roo.log(k);
19281         var match  = false;
19282         var csel = this.view.getSelectedNodes();
19283         var cselitem = false;
19284         if (csel.length) {
19285             var ix = this.view.indexOf(csel[0]);
19286             cselitem  = this.store.getAt(ix);
19287             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19288                 cselitem = false;
19289             }
19290             
19291         }
19292         
19293         this.store.each(function(v) { 
19294             if (cselitem) {
19295                 // start at existing selection.
19296                 if (cselitem.id == v.id) {
19297                     cselitem = false;
19298                 }
19299                 return;
19300             }
19301                 
19302             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19303                 match = this.store.indexOf(v);
19304                 return false;
19305             }
19306         }, this);
19307         
19308         if (match === false) {
19309             return true; // no more action?
19310         }
19311         // scroll to?
19312         this.view.select(match);
19313         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19314         sn.scrollIntoView(sn.dom.parentNode, false);
19315     } 
19316
19317     /** 
19318     * @cfg {Boolean} grow 
19319     * @hide 
19320     */
19321     /** 
19322     * @cfg {Number} growMin 
19323     * @hide 
19324     */
19325     /** 
19326     * @cfg {Number} growMax 
19327     * @hide 
19328     */
19329     /**
19330      * @hide
19331      * @method autoSize
19332      */
19333 });/*
19334  * Copyright(c) 2010-2012, Roo J Solutions Limited
19335  *
19336  * Licence LGPL
19337  *
19338  */
19339
19340 /**
19341  * @class Roo.form.ComboBoxArray
19342  * @extends Roo.form.TextField
19343  * A facebook style adder... for lists of email / people / countries  etc...
19344  * pick multiple items from a combo box, and shows each one.
19345  *
19346  *  Fred [x]  Brian [x]  [Pick another |v]
19347  *
19348  *
19349  *  For this to work: it needs various extra information
19350  *    - normal combo problay has
19351  *      name, hiddenName
19352  *    + displayField, valueField
19353  *
19354  *    For our purpose...
19355  *
19356  *
19357  *   If we change from 'extends' to wrapping...
19358  *   
19359  *  
19360  *
19361  
19362  
19363  * @constructor
19364  * Create a new ComboBoxArray.
19365  * @param {Object} config Configuration options
19366  */
19367  
19368
19369 Roo.form.ComboBoxArray = function(config)
19370 {
19371     this.addEvents({
19372         /**
19373          * @event beforeremove
19374          * Fires before remove the value from the list
19375              * @param {Roo.form.ComboBoxArray} _self This combo box array
19376              * @param {Roo.form.ComboBoxArray.Item} item removed item
19377              */
19378         'beforeremove' : true,
19379         /**
19380          * @event remove
19381          * Fires when remove the value from the list
19382              * @param {Roo.form.ComboBoxArray} _self This combo box array
19383              * @param {Roo.form.ComboBoxArray.Item} item removed item
19384              */
19385         'remove' : true
19386         
19387         
19388     });
19389     
19390     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19391     
19392     this.items = new Roo.util.MixedCollection(false);
19393     
19394     // construct the child combo...
19395     
19396     
19397     
19398     
19399    
19400     
19401 }
19402
19403  
19404 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19405
19406     /**
19407      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
19408      */
19409     
19410     lastData : false,
19411     
19412     // behavies liek a hiddne field
19413     inputType:      'hidden',
19414     /**
19415      * @cfg {Number} width The width of the box that displays the selected element
19416      */ 
19417     width:          300,
19418
19419     
19420     
19421     /**
19422      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19423      */
19424     name : false,
19425     /**
19426      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19427      */
19428     hiddenName : false,
19429       /**
19430      * @cfg {String} seperator    The value seperator normally ',' 
19431      */
19432     seperator : ',',
19433     
19434     // private the array of items that are displayed..
19435     items  : false,
19436     // private - the hidden field el.
19437     hiddenEl : false,
19438     // private - the filed el..
19439     el : false,
19440     
19441     //validateValue : function() { return true; }, // all values are ok!
19442     //onAddClick: function() { },
19443     
19444     onRender : function(ct, position) 
19445     {
19446         
19447         // create the standard hidden element
19448         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19449         
19450         
19451         // give fake names to child combo;
19452         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19453         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19454         
19455         this.combo = Roo.factory(this.combo, Roo.form);
19456         this.combo.onRender(ct, position);
19457         if (typeof(this.combo.width) != 'undefined') {
19458             this.combo.onResize(this.combo.width,0);
19459         }
19460         
19461         this.combo.initEvents();
19462         
19463         // assigned so form know we need to do this..
19464         this.store          = this.combo.store;
19465         this.valueField     = this.combo.valueField;
19466         this.displayField   = this.combo.displayField ;
19467         
19468         
19469         this.combo.wrap.addClass('x-cbarray-grp');
19470         
19471         var cbwrap = this.combo.wrap.createChild(
19472             {tag: 'div', cls: 'x-cbarray-cb'},
19473             this.combo.el.dom
19474         );
19475         
19476              
19477         this.hiddenEl = this.combo.wrap.createChild({
19478             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19479         });
19480         this.el = this.combo.wrap.createChild({
19481             tag: 'input',  type:'hidden' , name: this.name, value : ''
19482         });
19483          //   this.el.dom.removeAttribute("name");
19484         
19485         
19486         this.outerWrap = this.combo.wrap;
19487         this.wrap = cbwrap;
19488         
19489         this.outerWrap.setWidth(this.width);
19490         this.outerWrap.dom.removeChild(this.el.dom);
19491         
19492         this.wrap.dom.appendChild(this.el.dom);
19493         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19494         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19495         
19496         this.combo.trigger.setStyle('position','relative');
19497         this.combo.trigger.setStyle('left', '0px');
19498         this.combo.trigger.setStyle('top', '2px');
19499         
19500         this.combo.el.setStyle('vertical-align', 'text-bottom');
19501         
19502         //this.trigger.setStyle('vertical-align', 'top');
19503         
19504         // this should use the code from combo really... on('add' ....)
19505         if (this.adder) {
19506             
19507         
19508             this.adder = this.outerWrap.createChild(
19509                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19510             var _t = this;
19511             this.adder.on('click', function(e) {
19512                 _t.fireEvent('adderclick', this, e);
19513             }, _t);
19514         }
19515         //var _t = this;
19516         //this.adder.on('click', this.onAddClick, _t);
19517         
19518         
19519         this.combo.on('select', function(cb, rec, ix) {
19520             this.addItem(rec.data);
19521             
19522             cb.setValue('');
19523             cb.el.dom.value = '';
19524             //cb.lastData = rec.data;
19525             // add to list
19526             
19527         }, this);
19528         
19529         
19530     },
19531     
19532     
19533     getName: function()
19534     {
19535         // returns hidden if it's set..
19536         if (!this.rendered) {return ''};
19537         return  this.hiddenName ? this.hiddenName : this.name;
19538         
19539     },
19540     
19541     
19542     onResize: function(w, h){
19543         
19544         return;
19545         // not sure if this is needed..
19546         //this.combo.onResize(w,h);
19547         
19548         if(typeof w != 'number'){
19549             // we do not handle it!?!?
19550             return;
19551         }
19552         var tw = this.combo.trigger.getWidth();
19553         tw += this.addicon ? this.addicon.getWidth() : 0;
19554         tw += this.editicon ? this.editicon.getWidth() : 0;
19555         var x = w - tw;
19556         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19557             
19558         this.combo.trigger.setStyle('left', '0px');
19559         
19560         if(this.list && this.listWidth === undefined){
19561             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19562             this.list.setWidth(lw);
19563             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19564         }
19565         
19566     
19567         
19568     },
19569     
19570     addItem: function(rec)
19571     {
19572         var valueField = this.combo.valueField;
19573         var displayField = this.combo.displayField;
19574         
19575         if (this.items.indexOfKey(rec[valueField]) > -1) {
19576             //console.log("GOT " + rec.data.id);
19577             return;
19578         }
19579         
19580         var x = new Roo.form.ComboBoxArray.Item({
19581             //id : rec[this.idField],
19582             data : rec,
19583             displayField : displayField ,
19584             tipField : displayField ,
19585             cb : this
19586         });
19587         // use the 
19588         this.items.add(rec[valueField],x);
19589         // add it before the element..
19590         this.updateHiddenEl();
19591         x.render(this.outerWrap, this.wrap.dom);
19592         // add the image handler..
19593     },
19594     
19595     updateHiddenEl : function()
19596     {
19597         this.validate();
19598         if (!this.hiddenEl) {
19599             return;
19600         }
19601         var ar = [];
19602         var idField = this.combo.valueField;
19603         
19604         this.items.each(function(f) {
19605             ar.push(f.data[idField]);
19606         });
19607         this.hiddenEl.dom.value = ar.join(this.seperator);
19608         this.validate();
19609     },
19610     
19611     reset : function()
19612     {
19613         this.items.clear();
19614         
19615         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19616            el.remove();
19617         });
19618         
19619         this.el.dom.value = '';
19620         if (this.hiddenEl) {
19621             this.hiddenEl.dom.value = '';
19622         }
19623         
19624     },
19625     getValue: function()
19626     {
19627         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19628     },
19629     setValue: function(v) // not a valid action - must use addItems..
19630     {
19631         
19632         this.reset();
19633          
19634         if (this.store.isLocal && (typeof(v) == 'string')) {
19635             // then we can use the store to find the values..
19636             // comma seperated at present.. this needs to allow JSON based encoding..
19637             this.hiddenEl.value  = v;
19638             var v_ar = [];
19639             Roo.each(v.split(this.seperator), function(k) {
19640                 Roo.log("CHECK " + this.valueField + ',' + k);
19641                 var li = this.store.query(this.valueField, k);
19642                 if (!li.length) {
19643                     return;
19644                 }
19645                 var add = {};
19646                 add[this.valueField] = k;
19647                 add[this.displayField] = li.item(0).data[this.displayField];
19648                 
19649                 this.addItem(add);
19650             }, this) 
19651              
19652         }
19653         if (typeof(v) == 'object' ) {
19654             // then let's assume it's an array of objects..
19655             Roo.each(v, function(l) {
19656                 var add = l;
19657                 if (typeof(l) == 'string') {
19658                     add = {};
19659                     add[this.valueField] = l;
19660                     add[this.displayField] = l
19661                 }
19662                 this.addItem(add);
19663             }, this);
19664              
19665         }
19666         
19667         
19668     },
19669     setFromData: function(v)
19670     {
19671         // this recieves an object, if setValues is called.
19672         this.reset();
19673         this.el.dom.value = v[this.displayField];
19674         this.hiddenEl.dom.value = v[this.valueField];
19675         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19676             return;
19677         }
19678         var kv = v[this.valueField];
19679         var dv = v[this.displayField];
19680         kv = typeof(kv) != 'string' ? '' : kv;
19681         dv = typeof(dv) != 'string' ? '' : dv;
19682         
19683         
19684         var keys = kv.split(this.seperator);
19685         var display = dv.split(this.seperator);
19686         for (var i = 0 ; i < keys.length; i++) {
19687             add = {};
19688             add[this.valueField] = keys[i];
19689             add[this.displayField] = display[i];
19690             this.addItem(add);
19691         }
19692       
19693         
19694     },
19695     
19696     /**
19697      * Validates the combox array value
19698      * @return {Boolean} True if the value is valid, else false
19699      */
19700     validate : function(){
19701         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19702             this.clearInvalid();
19703             return true;
19704         }
19705         return false;
19706     },
19707     
19708     validateValue : function(value){
19709         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19710         
19711     },
19712     
19713     /*@
19714      * overide
19715      * 
19716      */
19717     isDirty : function() {
19718         if(this.disabled) {
19719             return false;
19720         }
19721         
19722         try {
19723             var d = Roo.decode(String(this.originalValue));
19724         } catch (e) {
19725             return String(this.getValue()) !== String(this.originalValue);
19726         }
19727         
19728         var originalValue = [];
19729         
19730         for (var i = 0; i < d.length; i++){
19731             originalValue.push(d[i][this.valueField]);
19732         }
19733         
19734         return String(this.getValue()) !== String(originalValue.join(this.seperator));
19735         
19736     }
19737     
19738 });
19739
19740
19741
19742 /**
19743  * @class Roo.form.ComboBoxArray.Item
19744  * @extends Roo.BoxComponent
19745  * A selected item in the list
19746  *  Fred [x]  Brian [x]  [Pick another |v]
19747  * 
19748  * @constructor
19749  * Create a new item.
19750  * @param {Object} config Configuration options
19751  */
19752  
19753 Roo.form.ComboBoxArray.Item = function(config) {
19754     config.id = Roo.id();
19755     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
19756 }
19757
19758 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
19759     data : {},
19760     cb: false,
19761     displayField : false,
19762     tipField : false,
19763     
19764     
19765     defaultAutoCreate : {
19766         tag: 'div',
19767         cls: 'x-cbarray-item',
19768         cn : [ 
19769             { tag: 'div' },
19770             {
19771                 tag: 'img',
19772                 width:16,
19773                 height : 16,
19774                 src : Roo.BLANK_IMAGE_URL ,
19775                 align: 'center'
19776             }
19777         ]
19778         
19779     },
19780     
19781  
19782     onRender : function(ct, position)
19783     {
19784         Roo.form.Field.superclass.onRender.call(this, ct, position);
19785         
19786         if(!this.el){
19787             var cfg = this.getAutoCreate();
19788             this.el = ct.createChild(cfg, position);
19789         }
19790         
19791         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
19792         
19793         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
19794             this.cb.renderer(this.data) :
19795             String.format('{0}',this.data[this.displayField]);
19796         
19797             
19798         this.el.child('div').dom.setAttribute('qtip',
19799                         String.format('{0}',this.data[this.tipField])
19800         );
19801         
19802         this.el.child('img').on('click', this.remove, this);
19803         
19804     },
19805    
19806     remove : function()
19807     {
19808         if(this.cb.disabled){
19809             return;
19810         }
19811         
19812         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
19813             this.cb.items.remove(this);
19814             this.el.child('img').un('click', this.remove, this);
19815             this.el.remove();
19816             this.cb.updateHiddenEl();
19817
19818             this.cb.fireEvent('remove', this.cb, this);
19819         }
19820         
19821     }
19822 });/*
19823  * RooJS Library 1.1.1
19824  * Copyright(c) 2008-2011  Alan Knowles
19825  *
19826  * License - LGPL
19827  */
19828  
19829
19830 /**
19831  * @class Roo.form.ComboNested
19832  * @extends Roo.form.ComboBox
19833  * A combobox for that allows selection of nested items in a list,
19834  * eg.
19835  *
19836  *  Book
19837  *    -> red
19838  *    -> green
19839  *  Table
19840  *    -> square
19841  *      ->red
19842  *      ->green
19843  *    -> rectangle
19844  *      ->green
19845  *      
19846  * 
19847  * @constructor
19848  * Create a new ComboNested
19849  * @param {Object} config Configuration options
19850  */
19851 Roo.form.ComboNested = function(config){
19852     Roo.form.ComboCheck.superclass.constructor.call(this, config);
19853     // should verify some data...
19854     // like
19855     // hiddenName = required..
19856     // displayField = required
19857     // valudField == required
19858     var req= [ 'hiddenName', 'displayField', 'valueField' ];
19859     var _t = this;
19860     Roo.each(req, function(e) {
19861         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
19862             throw "Roo.form.ComboNested : missing value for: " + e;
19863         }
19864     });
19865      
19866     
19867 };
19868
19869 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
19870    
19871     /*
19872      * @config {Number} max Number of columns to show
19873      */
19874     
19875     maxColumns : 3,
19876    
19877     list : null, // the outermost div..
19878     innerLists : null, // the
19879     views : null,
19880     stores : null,
19881     // private
19882     loadingChildren : false,
19883     
19884     onRender : function(ct, position)
19885     {
19886         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
19887         
19888         if(this.hiddenName){
19889             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
19890                     'before', true);
19891             this.hiddenField.value =
19892                 this.hiddenValue !== undefined ? this.hiddenValue :
19893                 this.value !== undefined ? this.value : '';
19894
19895             // prevent input submission
19896             this.el.dom.removeAttribute('name');
19897              
19898              
19899         }
19900         
19901         if(Roo.isGecko){
19902             this.el.dom.setAttribute('autocomplete', 'off');
19903         }
19904
19905         var cls = 'x-combo-list';
19906
19907         this.list = new Roo.Layer({
19908             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
19909         });
19910
19911         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
19912         this.list.setWidth(lw);
19913         this.list.swallowEvent('mousewheel');
19914         this.assetHeight = 0;
19915
19916         if(this.title){
19917             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
19918             this.assetHeight += this.header.getHeight();
19919         }
19920         this.innerLists = [];
19921         this.views = [];
19922         this.stores = [];
19923         for (var i =0 ; i < this.maxColumns; i++) {
19924             this.onRenderList( cls, i);
19925         }
19926         
19927         // always needs footer, as we are going to have an 'OK' button.
19928         this.footer = this.list.createChild({cls:cls+'-ft'});
19929         this.pageTb = new Roo.Toolbar(this.footer);  
19930         var _this = this;
19931         this.pageTb.add(  {
19932             
19933             text: 'Done',
19934             handler: function()
19935             {
19936                 _this.collapse();
19937             }
19938         });
19939         
19940         if ( this.allowBlank && !this.disableClear) {
19941             
19942             this.pageTb.add(new Roo.Toolbar.Fill(), {
19943                 cls: 'x-btn-icon x-btn-clear',
19944                 text: '&#160;',
19945                 handler: function()
19946                 {
19947                     _this.collapse();
19948                     _this.clearValue();
19949                     _this.onSelect(false, -1);
19950                 }
19951             });
19952         }
19953         if (this.footer) {
19954             this.assetHeight += this.footer.getHeight();
19955         }
19956         
19957     },
19958     onRenderList : function (  cls, i)
19959     {
19960         
19961         var lw = Math.floor(
19962                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
19963         );
19964         
19965         this.list.setWidth(lw); // default to '1'
19966
19967         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
19968         //il.on('mouseover', this.onViewOver, this, { list:  i });
19969         //il.on('mousemove', this.onViewMove, this, { list:  i });
19970         il.setWidth(lw);
19971         il.setStyle({ 'overflow-x' : 'hidden'});
19972
19973         if(!this.tpl){
19974             this.tpl = new Roo.Template({
19975                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
19976                 isEmpty: function (value, allValues) {
19977                     //Roo.log(value);
19978                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
19979                     return dl ? 'has-children' : 'no-children'
19980                 }
19981             });
19982         }
19983         
19984         var store  = this.store;
19985         if (i > 0) {
19986             store  = new Roo.data.SimpleStore({
19987                 //fields : this.store.reader.meta.fields,
19988                 reader : this.store.reader,
19989                 data : [ ]
19990             });
19991         }
19992         this.stores[i]  = store;
19993                   
19994         var view = this.views[i] = new Roo.View(
19995             il,
19996             this.tpl,
19997             {
19998                 singleSelect:true,
19999                 store: store,
20000                 selectedClass: this.selectedClass
20001             }
20002         );
20003         view.getEl().setWidth(lw);
20004         view.getEl().setStyle({
20005             position: i < 1 ? 'relative' : 'absolute',
20006             top: 0,
20007             left: (i * lw ) + 'px',
20008             display : i > 0 ? 'none' : 'block'
20009         });
20010         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
20011         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
20012         //view.on('click', this.onViewClick, this, { list : i });
20013
20014         store.on('beforeload', this.onBeforeLoad, this);
20015         store.on('load',  this.onLoad, this, { list  : i});
20016         store.on('loadexception', this.onLoadException, this);
20017
20018         // hide the other vies..
20019         
20020         
20021         
20022     },
20023       
20024     restrictHeight : function()
20025     {
20026         var mh = 0;
20027         Roo.each(this.innerLists, function(il,i) {
20028             var el = this.views[i].getEl();
20029             el.dom.style.height = '';
20030             var inner = el.dom;
20031             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
20032             // only adjust heights on other ones..
20033             mh = Math.max(h, mh);
20034             if (i < 1) {
20035                 
20036                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20037                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20038                
20039             }
20040             
20041             
20042         }, this);
20043         
20044         this.list.beginUpdate();
20045         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20046         this.list.alignTo(this.el, this.listAlign);
20047         this.list.endUpdate();
20048         
20049     },
20050      
20051     
20052     // -- store handlers..
20053     // private
20054     onBeforeLoad : function()
20055     {
20056         if(!this.hasFocus){
20057             return;
20058         }
20059         this.innerLists[0].update(this.loadingText ?
20060                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20061         this.restrictHeight();
20062         this.selectedIndex = -1;
20063     },
20064     // private
20065     onLoad : function(a,b,c,d)
20066     {
20067         if (!this.loadingChildren) {
20068             // then we are loading the top level. - hide the children
20069             for (var i = 1;i < this.views.length; i++) {
20070                 this.views[i].getEl().setStyle({ display : 'none' });
20071             }
20072             var lw = Math.floor(
20073                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20074             );
20075         
20076              this.list.setWidth(lw); // default to '1'
20077
20078             
20079         }
20080         if(!this.hasFocus){
20081             return;
20082         }
20083         
20084         if(this.store.getCount() > 0) {
20085             this.expand();
20086             this.restrictHeight();   
20087         } else {
20088             this.onEmptyResults();
20089         }
20090         
20091         if (!this.loadingChildren) {
20092             this.selectActive();
20093         }
20094         /*
20095         this.stores[1].loadData([]);
20096         this.stores[2].loadData([]);
20097         this.views
20098         */    
20099     
20100         //this.el.focus();
20101     },
20102     
20103     
20104     // private
20105     onLoadException : function()
20106     {
20107         this.collapse();
20108         Roo.log(this.store.reader.jsonData);
20109         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20110             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20111         }
20112         
20113         
20114     },
20115     // no cleaning of leading spaces on blur here.
20116     cleanLeadingSpace : function(e) { },
20117     
20118
20119     onSelectChange : function (view, sels, opts )
20120     {
20121         var ix = view.getSelectedIndexes();
20122          
20123         if (opts.list > this.maxColumns - 2) {
20124             if (view.store.getCount()<  1) {
20125                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
20126
20127             } else  {
20128                 if (ix.length) {
20129                     // used to clear ?? but if we are loading unselected 
20130                     this.setFromData(view.store.getAt(ix[0]).data);
20131                 }
20132                 
20133             }
20134             
20135             return;
20136         }
20137         
20138         if (!ix.length) {
20139             // this get's fired when trigger opens..
20140            // this.setFromData({});
20141             var str = this.stores[opts.list+1];
20142             str.data.clear(); // removeall wihtout the fire events..
20143             return;
20144         }
20145         
20146         var rec = view.store.getAt(ix[0]);
20147          
20148         this.setFromData(rec.data);
20149         this.fireEvent('select', this, rec, ix[0]);
20150         
20151         var lw = Math.floor(
20152              (
20153                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
20154              ) / this.maxColumns
20155         );
20156         this.loadingChildren = true;
20157         this.stores[opts.list+1].loadDataFromChildren( rec );
20158         this.loadingChildren = false;
20159         var dl = this.stores[opts.list+1]. getTotalCount();
20160         
20161         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20162         
20163         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20164         for (var i = opts.list+2; i < this.views.length;i++) {
20165             this.views[i].getEl().setStyle({ display : 'none' });
20166         }
20167         
20168         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20169         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
20170         
20171         if (this.isLoading) {
20172            // this.selectActive(opts.list);
20173         }
20174          
20175     },
20176     
20177     
20178     
20179     
20180     onDoubleClick : function()
20181     {
20182         this.collapse(); //??
20183     },
20184     
20185      
20186     
20187     
20188     
20189     // private
20190     recordToStack : function(store, prop, value, stack)
20191     {
20192         var cstore = new Roo.data.SimpleStore({
20193             //fields : this.store.reader.meta.fields, // we need array reader.. for
20194             reader : this.store.reader,
20195             data : [ ]
20196         });
20197         var _this = this;
20198         var record  = false;
20199         var srec = false;
20200         if(store.getCount() < 1){
20201             return false;
20202         }
20203         store.each(function(r){
20204             if(r.data[prop] == value){
20205                 record = r;
20206             srec = r;
20207                 return false;
20208             }
20209             if (r.data.cn && r.data.cn.length) {
20210                 cstore.loadDataFromChildren( r);
20211                 var cret = _this.recordToStack(cstore, prop, value, stack);
20212                 if (cret !== false) {
20213                     record = cret;
20214                     srec = r;
20215                     return false;
20216                 }
20217             }
20218              
20219             return true;
20220         });
20221         if (record == false) {
20222             return false
20223         }
20224         stack.unshift(srec);
20225         return record;
20226     },
20227     
20228     /*
20229      * find the stack of stores that match our value.
20230      *
20231      * 
20232      */
20233     
20234     selectActive : function ()
20235     {
20236         // if store is not loaded, then we will need to wait for that to happen first.
20237         var stack = [];
20238         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
20239         for (var i = 0; i < stack.length; i++ ) {
20240             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
20241         }
20242         
20243     }
20244         
20245          
20246     
20247     
20248     
20249     
20250 });/*
20251  * Based on:
20252  * Ext JS Library 1.1.1
20253  * Copyright(c) 2006-2007, Ext JS, LLC.
20254  *
20255  * Originally Released Under LGPL - original licence link has changed is not relivant.
20256  *
20257  * Fork - LGPL
20258  * <script type="text/javascript">
20259  */
20260 /**
20261  * @class Roo.form.Checkbox
20262  * @extends Roo.form.Field
20263  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20264  * @constructor
20265  * Creates a new Checkbox
20266  * @param {Object} config Configuration options
20267  */
20268 Roo.form.Checkbox = function(config){
20269     Roo.form.Checkbox.superclass.constructor.call(this, config);
20270     this.addEvents({
20271         /**
20272          * @event check
20273          * Fires when the checkbox is checked or unchecked.
20274              * @param {Roo.form.Checkbox} this This checkbox
20275              * @param {Boolean} checked The new checked value
20276              */
20277         check : true
20278     });
20279 };
20280
20281 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20282     /**
20283      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20284      */
20285     focusClass : undefined,
20286     /**
20287      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20288      */
20289     fieldClass: "x-form-field",
20290     /**
20291      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20292      */
20293     checked: false,
20294     /**
20295      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20296      * {tag: "input", type: "checkbox", autocomplete: "off"})
20297      */
20298     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20299     /**
20300      * @cfg {String} boxLabel The text that appears beside the checkbox
20301      */
20302     boxLabel : "",
20303     /**
20304      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20305      */  
20306     inputValue : '1',
20307     /**
20308      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20309      */
20310      valueOff: '0', // value when not checked..
20311
20312     actionMode : 'viewEl', 
20313     //
20314     // private
20315     itemCls : 'x-menu-check-item x-form-item',
20316     groupClass : 'x-menu-group-item',
20317     inputType : 'hidden',
20318     
20319     
20320     inSetChecked: false, // check that we are not calling self...
20321     
20322     inputElement: false, // real input element?
20323     basedOn: false, // ????
20324     
20325     isFormField: true, // not sure where this is needed!!!!
20326
20327     onResize : function(){
20328         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20329         if(!this.boxLabel){
20330             this.el.alignTo(this.wrap, 'c-c');
20331         }
20332     },
20333
20334     initEvents : function(){
20335         Roo.form.Checkbox.superclass.initEvents.call(this);
20336         this.el.on("click", this.onClick,  this);
20337         this.el.on("change", this.onClick,  this);
20338     },
20339
20340
20341     getResizeEl : function(){
20342         return this.wrap;
20343     },
20344
20345     getPositionEl : function(){
20346         return this.wrap;
20347     },
20348
20349     // private
20350     onRender : function(ct, position){
20351         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20352         /*
20353         if(this.inputValue !== undefined){
20354             this.el.dom.value = this.inputValue;
20355         }
20356         */
20357         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20358         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20359         var viewEl = this.wrap.createChild({ 
20360             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20361         this.viewEl = viewEl;   
20362         this.wrap.on('click', this.onClick,  this); 
20363         
20364         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20365         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20366         
20367         
20368         
20369         if(this.boxLabel){
20370             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20371         //    viewEl.on('click', this.onClick,  this); 
20372         }
20373         //if(this.checked){
20374             this.setChecked(this.checked);
20375         //}else{
20376             //this.checked = this.el.dom;
20377         //}
20378
20379     },
20380
20381     // private
20382     initValue : Roo.emptyFn,
20383
20384     /**
20385      * Returns the checked state of the checkbox.
20386      * @return {Boolean} True if checked, else false
20387      */
20388     getValue : function(){
20389         if(this.el){
20390             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20391         }
20392         return this.valueOff;
20393         
20394     },
20395
20396         // private
20397     onClick : function(){ 
20398         if (this.disabled) {
20399             return;
20400         }
20401         this.setChecked(!this.checked);
20402
20403         //if(this.el.dom.checked != this.checked){
20404         //    this.setValue(this.el.dom.checked);
20405        // }
20406     },
20407
20408     /**
20409      * Sets the checked state of the checkbox.
20410      * On is always based on a string comparison between inputValue and the param.
20411      * @param {Boolean/String} value - the value to set 
20412      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20413      */
20414     setValue : function(v,suppressEvent){
20415         
20416         
20417         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20418         //if(this.el && this.el.dom){
20419         //    this.el.dom.checked = this.checked;
20420         //    this.el.dom.defaultChecked = this.checked;
20421         //}
20422         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20423         //this.fireEvent("check", this, this.checked);
20424     },
20425     // private..
20426     setChecked : function(state,suppressEvent)
20427     {
20428         if (this.inSetChecked) {
20429             this.checked = state;
20430             return;
20431         }
20432         
20433     
20434         if(this.wrap){
20435             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20436         }
20437         this.checked = state;
20438         if(suppressEvent !== true){
20439             this.fireEvent('check', this, state);
20440         }
20441         this.inSetChecked = true;
20442         this.el.dom.value = state ? this.inputValue : this.valueOff;
20443         this.inSetChecked = false;
20444         
20445     },
20446     // handle setting of hidden value by some other method!!?!?
20447     setFromHidden: function()
20448     {
20449         if(!this.el){
20450             return;
20451         }
20452         //console.log("SET FROM HIDDEN");
20453         //alert('setFrom hidden');
20454         this.setValue(this.el.dom.value);
20455     },
20456     
20457     onDestroy : function()
20458     {
20459         if(this.viewEl){
20460             Roo.get(this.viewEl).remove();
20461         }
20462          
20463         Roo.form.Checkbox.superclass.onDestroy.call(this);
20464     },
20465     
20466     setBoxLabel : function(str)
20467     {
20468         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20469     }
20470
20471 });/*
20472  * Based on:
20473  * Ext JS Library 1.1.1
20474  * Copyright(c) 2006-2007, Ext JS, LLC.
20475  *
20476  * Originally Released Under LGPL - original licence link has changed is not relivant.
20477  *
20478  * Fork - LGPL
20479  * <script type="text/javascript">
20480  */
20481  
20482 /**
20483  * @class Roo.form.Radio
20484  * @extends Roo.form.Checkbox
20485  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20486  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20487  * @constructor
20488  * Creates a new Radio
20489  * @param {Object} config Configuration options
20490  */
20491 Roo.form.Radio = function(){
20492     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20493 };
20494 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20495     inputType: 'radio',
20496
20497     /**
20498      * If this radio is part of a group, it will return the selected value
20499      * @return {String}
20500      */
20501     getGroupValue : function(){
20502         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20503     },
20504     
20505     
20506     onRender : function(ct, position){
20507         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20508         
20509         if(this.inputValue !== undefined){
20510             this.el.dom.value = this.inputValue;
20511         }
20512          
20513         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20514         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20515         //var viewEl = this.wrap.createChild({ 
20516         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20517         //this.viewEl = viewEl;   
20518         //this.wrap.on('click', this.onClick,  this); 
20519         
20520         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20521         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20522         
20523         
20524         
20525         if(this.boxLabel){
20526             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20527         //    viewEl.on('click', this.onClick,  this); 
20528         }
20529          if(this.checked){
20530             this.el.dom.checked =   'checked' ;
20531         }
20532          
20533     } 
20534     
20535     
20536 });//<script type="text/javascript">
20537
20538 /*
20539  * Based  Ext JS Library 1.1.1
20540  * Copyright(c) 2006-2007, Ext JS, LLC.
20541  * LGPL
20542  *
20543  */
20544  
20545 /**
20546  * @class Roo.HtmlEditorCore
20547  * @extends Roo.Component
20548  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20549  *
20550  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20551  */
20552
20553 Roo.HtmlEditorCore = function(config){
20554     
20555     
20556     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20557     
20558     
20559     this.addEvents({
20560         /**
20561          * @event initialize
20562          * Fires when the editor is fully initialized (including the iframe)
20563          * @param {Roo.HtmlEditorCore} this
20564          */
20565         initialize: true,
20566         /**
20567          * @event activate
20568          * Fires when the editor is first receives the focus. Any insertion must wait
20569          * until after this event.
20570          * @param {Roo.HtmlEditorCore} this
20571          */
20572         activate: true,
20573          /**
20574          * @event beforesync
20575          * Fires before the textarea is updated with content from the editor iframe. Return false
20576          * to cancel the sync.
20577          * @param {Roo.HtmlEditorCore} this
20578          * @param {String} html
20579          */
20580         beforesync: true,
20581          /**
20582          * @event beforepush
20583          * Fires before the iframe editor is updated with content from the textarea. Return false
20584          * to cancel the push.
20585          * @param {Roo.HtmlEditorCore} this
20586          * @param {String} html
20587          */
20588         beforepush: true,
20589          /**
20590          * @event sync
20591          * Fires when the textarea is updated with content from the editor iframe.
20592          * @param {Roo.HtmlEditorCore} this
20593          * @param {String} html
20594          */
20595         sync: true,
20596          /**
20597          * @event push
20598          * Fires when the iframe editor is updated with content from the textarea.
20599          * @param {Roo.HtmlEditorCore} this
20600          * @param {String} html
20601          */
20602         push: true,
20603         
20604         /**
20605          * @event editorevent
20606          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20607          * @param {Roo.HtmlEditorCore} this
20608          */
20609         editorevent: true
20610         
20611     });
20612     
20613     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20614     
20615     // defaults : white / black...
20616     this.applyBlacklists();
20617     
20618     
20619     
20620 };
20621
20622
20623 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20624
20625
20626      /**
20627      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20628      */
20629     
20630     owner : false,
20631     
20632      /**
20633      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20634      *                        Roo.resizable.
20635      */
20636     resizable : false,
20637      /**
20638      * @cfg {Number} height (in pixels)
20639      */   
20640     height: 300,
20641    /**
20642      * @cfg {Number} width (in pixels)
20643      */   
20644     width: 500,
20645     
20646     /**
20647      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20648      * 
20649      */
20650     stylesheets: false,
20651     
20652     /**
20653      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
20654      */
20655     allowComments: false,
20656     // id of frame..
20657     frameId: false,
20658     
20659     // private properties
20660     validationEvent : false,
20661     deferHeight: true,
20662     initialized : false,
20663     activated : false,
20664     sourceEditMode : false,
20665     onFocus : Roo.emptyFn,
20666     iframePad:3,
20667     hideMode:'offsets',
20668     
20669     clearUp: true,
20670     
20671     // blacklist + whitelisted elements..
20672     black: false,
20673     white: false,
20674      
20675     bodyCls : '',
20676
20677     /**
20678      * Protected method that will not generally be called directly. It
20679      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20680      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20681      */
20682     getDocMarkup : function(){
20683         // body styles..
20684         var st = '';
20685         
20686         // inherit styels from page...?? 
20687         if (this.stylesheets === false) {
20688             
20689             Roo.get(document.head).select('style').each(function(node) {
20690                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20691             });
20692             
20693             Roo.get(document.head).select('link').each(function(node) { 
20694                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20695             });
20696             
20697         } else if (!this.stylesheets.length) {
20698                 // simple..
20699                 st = '<style type="text/css">' +
20700                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20701                    '</style>';
20702         } else {
20703             for (var i in this.stylesheets) { 
20704                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
20705             }
20706             
20707         }
20708         
20709         st +=  '<style type="text/css">' +
20710             'IMG { cursor: pointer } ' +
20711         '</style>';
20712
20713         var cls = 'roo-htmleditor-body';
20714         
20715         if(this.bodyCls.length){
20716             cls += ' ' + this.bodyCls;
20717         }
20718         
20719         return '<html><head>' + st  +
20720             //<style type="text/css">' +
20721             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20722             //'</style>' +
20723             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
20724     },
20725
20726     // private
20727     onRender : function(ct, position)
20728     {
20729         var _t = this;
20730         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20731         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20732         
20733         
20734         this.el.dom.style.border = '0 none';
20735         this.el.dom.setAttribute('tabIndex', -1);
20736         this.el.addClass('x-hidden hide');
20737         
20738         
20739         
20740         if(Roo.isIE){ // fix IE 1px bogus margin
20741             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20742         }
20743        
20744         
20745         this.frameId = Roo.id();
20746         
20747          
20748         
20749         var iframe = this.owner.wrap.createChild({
20750             tag: 'iframe',
20751             cls: 'form-control', // bootstrap..
20752             id: this.frameId,
20753             name: this.frameId,
20754             frameBorder : 'no',
20755             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20756         }, this.el
20757         );
20758         
20759         
20760         this.iframe = iframe.dom;
20761
20762          this.assignDocWin();
20763         
20764         this.doc.designMode = 'on';
20765        
20766         this.doc.open();
20767         this.doc.write(this.getDocMarkup());
20768         this.doc.close();
20769
20770         
20771         var task = { // must defer to wait for browser to be ready
20772             run : function(){
20773                 //console.log("run task?" + this.doc.readyState);
20774                 this.assignDocWin();
20775                 if(this.doc.body || this.doc.readyState == 'complete'){
20776                     try {
20777                         this.doc.designMode="on";
20778                     } catch (e) {
20779                         return;
20780                     }
20781                     Roo.TaskMgr.stop(task);
20782                     this.initEditor.defer(10, this);
20783                 }
20784             },
20785             interval : 10,
20786             duration: 10000,
20787             scope: this
20788         };
20789         Roo.TaskMgr.start(task);
20790
20791     },
20792
20793     // private
20794     onResize : function(w, h)
20795     {
20796          Roo.log('resize: ' +w + ',' + h );
20797         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20798         if(!this.iframe){
20799             return;
20800         }
20801         if(typeof w == 'number'){
20802             
20803             this.iframe.style.width = w + 'px';
20804         }
20805         if(typeof h == 'number'){
20806             
20807             this.iframe.style.height = h + 'px';
20808             if(this.doc){
20809                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20810             }
20811         }
20812         
20813     },
20814
20815     /**
20816      * Toggles the editor between standard and source edit mode.
20817      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20818      */
20819     toggleSourceEdit : function(sourceEditMode){
20820         
20821         this.sourceEditMode = sourceEditMode === true;
20822         
20823         if(this.sourceEditMode){
20824  
20825             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20826             
20827         }else{
20828             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20829             //this.iframe.className = '';
20830             this.deferFocus();
20831         }
20832         //this.setSize(this.owner.wrap.getSize());
20833         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20834     },
20835
20836     
20837   
20838
20839     /**
20840      * Protected method that will not generally be called directly. If you need/want
20841      * custom HTML cleanup, this is the method you should override.
20842      * @param {String} html The HTML to be cleaned
20843      * return {String} The cleaned HTML
20844      */
20845     cleanHtml : function(html){
20846         html = String(html);
20847         if(html.length > 5){
20848             if(Roo.isSafari){ // strip safari nonsense
20849                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20850             }
20851         }
20852         if(html == '&nbsp;'){
20853             html = '';
20854         }
20855         return html;
20856     },
20857
20858     /**
20859      * HTML Editor -> Textarea
20860      * Protected method that will not generally be called directly. Syncs the contents
20861      * of the editor iframe with the textarea.
20862      */
20863     syncValue : function(){
20864         if(this.initialized){
20865             var bd = (this.doc.body || this.doc.documentElement);
20866             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20867             var html = bd.innerHTML;
20868             if(Roo.isSafari){
20869                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20870                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20871                 if(m && m[1]){
20872                     html = '<div style="'+m[0]+'">' + html + '</div>';
20873                 }
20874             }
20875             html = this.cleanHtml(html);
20876             // fix up the special chars.. normaly like back quotes in word...
20877             // however we do not want to do this with chinese..
20878             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
20879                 
20880                 var cc = match.charCodeAt();
20881
20882                 // Get the character value, handling surrogate pairs
20883                 if (match.length == 2) {
20884                     // It's a surrogate pair, calculate the Unicode code point
20885                     var high = match.charCodeAt(0) - 0xD800;
20886                     var low  = match.charCodeAt(1) - 0xDC00;
20887                     cc = (high * 0x400) + low + 0x10000;
20888                 }  else if (
20889                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20890                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20891                     (cc >= 0xf900 && cc < 0xfb00 )
20892                 ) {
20893                         return match;
20894                 }  
20895          
20896                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
20897                 return "&#" + cc + ";";
20898                 
20899                 
20900             });
20901             
20902             
20903              
20904             if(this.owner.fireEvent('beforesync', this, html) !== false){
20905                 this.el.dom.value = html;
20906                 this.owner.fireEvent('sync', this, html);
20907             }
20908         }
20909     },
20910
20911     /**
20912      * Protected method that will not generally be called directly. Pushes the value of the textarea
20913      * into the iframe editor.
20914      */
20915     pushValue : function(){
20916         if(this.initialized){
20917             var v = this.el.dom.value.trim();
20918             
20919 //            if(v.length < 1){
20920 //                v = '&#160;';
20921 //            }
20922             
20923             if(this.owner.fireEvent('beforepush', this, v) !== false){
20924                 var d = (this.doc.body || this.doc.documentElement);
20925                 d.innerHTML = v;
20926                 this.cleanUpPaste();
20927                 this.el.dom.value = d.innerHTML;
20928                 this.owner.fireEvent('push', this, v);
20929             }
20930         }
20931     },
20932
20933     // private
20934     deferFocus : function(){
20935         this.focus.defer(10, this);
20936     },
20937
20938     // doc'ed in Field
20939     focus : function(){
20940         if(this.win && !this.sourceEditMode){
20941             this.win.focus();
20942         }else{
20943             this.el.focus();
20944         }
20945     },
20946     
20947     assignDocWin: function()
20948     {
20949         var iframe = this.iframe;
20950         
20951          if(Roo.isIE){
20952             this.doc = iframe.contentWindow.document;
20953             this.win = iframe.contentWindow;
20954         } else {
20955 //            if (!Roo.get(this.frameId)) {
20956 //                return;
20957 //            }
20958 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20959 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20960             
20961             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20962                 return;
20963             }
20964             
20965             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20966             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20967         }
20968     },
20969     
20970     // private
20971     initEditor : function(){
20972         //console.log("INIT EDITOR");
20973         this.assignDocWin();
20974         
20975         
20976         
20977         this.doc.designMode="on";
20978         this.doc.open();
20979         this.doc.write(this.getDocMarkup());
20980         this.doc.close();
20981         
20982         var dbody = (this.doc.body || this.doc.documentElement);
20983         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20984         // this copies styles from the containing element into thsi one..
20985         // not sure why we need all of this..
20986         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20987         
20988         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20989         //ss['background-attachment'] = 'fixed'; // w3c
20990         dbody.bgProperties = 'fixed'; // ie
20991         //Roo.DomHelper.applyStyles(dbody, ss);
20992         Roo.EventManager.on(this.doc, {
20993             //'mousedown': this.onEditorEvent,
20994             'mouseup': this.onEditorEvent,
20995             'dblclick': this.onEditorEvent,
20996             'click': this.onEditorEvent,
20997             'keyup': this.onEditorEvent,
20998             buffer:100,
20999             scope: this
21000         });
21001         if(Roo.isGecko){
21002             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21003         }
21004         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21005             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21006         }
21007         this.initialized = true;
21008
21009         this.owner.fireEvent('initialize', this);
21010         this.pushValue();
21011     },
21012
21013     // private
21014     onDestroy : function(){
21015         
21016         
21017         
21018         if(this.rendered){
21019             
21020             //for (var i =0; i < this.toolbars.length;i++) {
21021             //    // fixme - ask toolbars for heights?
21022             //    this.toolbars[i].onDestroy();
21023            // }
21024             
21025             //this.wrap.dom.innerHTML = '';
21026             //this.wrap.remove();
21027         }
21028     },
21029
21030     // private
21031     onFirstFocus : function(){
21032         
21033         this.assignDocWin();
21034         
21035         
21036         this.activated = true;
21037          
21038     
21039         if(Roo.isGecko){ // prevent silly gecko errors
21040             this.win.focus();
21041             var s = this.win.getSelection();
21042             if(!s.focusNode || s.focusNode.nodeType != 3){
21043                 var r = s.getRangeAt(0);
21044                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21045                 r.collapse(true);
21046                 this.deferFocus();
21047             }
21048             try{
21049                 this.execCmd('useCSS', true);
21050                 this.execCmd('styleWithCSS', false);
21051             }catch(e){}
21052         }
21053         this.owner.fireEvent('activate', this);
21054     },
21055
21056     // private
21057     adjustFont: function(btn){
21058         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21059         //if(Roo.isSafari){ // safari
21060         //    adjust *= 2;
21061        // }
21062         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21063         if(Roo.isSafari){ // safari
21064             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21065             v =  (v < 10) ? 10 : v;
21066             v =  (v > 48) ? 48 : v;
21067             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21068             
21069         }
21070         
21071         
21072         v = Math.max(1, v+adjust);
21073         
21074         this.execCmd('FontSize', v  );
21075     },
21076
21077     onEditorEvent : function(e)
21078     {
21079         this.owner.fireEvent('editorevent', this, e);
21080       //  this.updateToolbar();
21081         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21082     },
21083
21084     insertTag : function(tg)
21085     {
21086         // could be a bit smarter... -> wrap the current selected tRoo..
21087         if (tg.toLowerCase() == 'span' ||
21088             tg.toLowerCase() == 'code' ||
21089             tg.toLowerCase() == 'sup' ||
21090             tg.toLowerCase() == 'sub' 
21091             ) {
21092             
21093             range = this.createRange(this.getSelection());
21094             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21095             wrappingNode.appendChild(range.extractContents());
21096             range.insertNode(wrappingNode);
21097
21098             return;
21099             
21100             
21101             
21102         }
21103         this.execCmd("formatblock",   tg);
21104         
21105     },
21106     
21107     insertText : function(txt)
21108     {
21109         
21110         
21111         var range = this.createRange();
21112         range.deleteContents();
21113                //alert(Sender.getAttribute('label'));
21114                
21115         range.insertNode(this.doc.createTextNode(txt));
21116     } ,
21117     
21118      
21119
21120     /**
21121      * Executes a Midas editor command on the editor document and performs necessary focus and
21122      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21123      * @param {String} cmd The Midas command
21124      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21125      */
21126     relayCmd : function(cmd, value){
21127         this.win.focus();
21128         this.execCmd(cmd, value);
21129         this.owner.fireEvent('editorevent', this);
21130         //this.updateToolbar();
21131         this.owner.deferFocus();
21132     },
21133
21134     /**
21135      * Executes a Midas editor command directly on the editor document.
21136      * For visual commands, you should use {@link #relayCmd} instead.
21137      * <b>This should only be called after the editor is initialized.</b>
21138      * @param {String} cmd The Midas command
21139      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21140      */
21141     execCmd : function(cmd, value){
21142         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21143         this.syncValue();
21144     },
21145  
21146  
21147    
21148     /**
21149      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21150      * to insert tRoo.
21151      * @param {String} text | dom node.. 
21152      */
21153     insertAtCursor : function(text)
21154     {
21155         
21156         if(!this.activated){
21157             return;
21158         }
21159         /*
21160         if(Roo.isIE){
21161             this.win.focus();
21162             var r = this.doc.selection.createRange();
21163             if(r){
21164                 r.collapse(true);
21165                 r.pasteHTML(text);
21166                 this.syncValue();
21167                 this.deferFocus();
21168             
21169             }
21170             return;
21171         }
21172         */
21173         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21174             this.win.focus();
21175             
21176             
21177             // from jquery ui (MIT licenced)
21178             var range, node;
21179             var win = this.win;
21180             
21181             if (win.getSelection && win.getSelection().getRangeAt) {
21182                 range = win.getSelection().getRangeAt(0);
21183                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21184                 range.insertNode(node);
21185             } else if (win.document.selection && win.document.selection.createRange) {
21186                 // no firefox support
21187                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21188                 win.document.selection.createRange().pasteHTML(txt);
21189             } else {
21190                 // no firefox support
21191                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21192                 this.execCmd('InsertHTML', txt);
21193             } 
21194             
21195             this.syncValue();
21196             
21197             this.deferFocus();
21198         }
21199     },
21200  // private
21201     mozKeyPress : function(e){
21202         if(e.ctrlKey){
21203             var c = e.getCharCode(), cmd;
21204           
21205             if(c > 0){
21206                 c = String.fromCharCode(c).toLowerCase();
21207                 switch(c){
21208                     case 'b':
21209                         cmd = 'bold';
21210                         break;
21211                     case 'i':
21212                         cmd = 'italic';
21213                         break;
21214                     
21215                     case 'u':
21216                         cmd = 'underline';
21217                         break;
21218                     
21219                     case 'v':
21220                         this.cleanUpPaste.defer(100, this);
21221                         return;
21222                         
21223                 }
21224                 if(cmd){
21225                     this.win.focus();
21226                     this.execCmd(cmd);
21227                     this.deferFocus();
21228                     e.preventDefault();
21229                 }
21230                 
21231             }
21232         }
21233     },
21234
21235     // private
21236     fixKeys : function(){ // load time branching for fastest keydown performance
21237         if(Roo.isIE){
21238             return function(e){
21239                 var k = e.getKey(), r;
21240                 if(k == e.TAB){
21241                     e.stopEvent();
21242                     r = this.doc.selection.createRange();
21243                     if(r){
21244                         r.collapse(true);
21245                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21246                         this.deferFocus();
21247                     }
21248                     return;
21249                 }
21250                 
21251                 if(k == e.ENTER){
21252                     r = this.doc.selection.createRange();
21253                     if(r){
21254                         var target = r.parentElement();
21255                         if(!target || target.tagName.toLowerCase() != 'li'){
21256                             e.stopEvent();
21257                             r.pasteHTML('<br />');
21258                             r.collapse(false);
21259                             r.select();
21260                         }
21261                     }
21262                 }
21263                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21264                     this.cleanUpPaste.defer(100, this);
21265                     return;
21266                 }
21267                 
21268                 
21269             };
21270         }else if(Roo.isOpera){
21271             return function(e){
21272                 var k = e.getKey();
21273                 if(k == e.TAB){
21274                     e.stopEvent();
21275                     this.win.focus();
21276                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21277                     this.deferFocus();
21278                 }
21279                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21280                     this.cleanUpPaste.defer(100, this);
21281                     return;
21282                 }
21283                 
21284             };
21285         }else if(Roo.isSafari){
21286             return function(e){
21287                 var k = e.getKey();
21288                 
21289                 if(k == e.TAB){
21290                     e.stopEvent();
21291                     this.execCmd('InsertText','\t');
21292                     this.deferFocus();
21293                     return;
21294                 }
21295                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21296                     this.cleanUpPaste.defer(100, this);
21297                     return;
21298                 }
21299                 
21300              };
21301         }
21302     }(),
21303     
21304     getAllAncestors: function()
21305     {
21306         var p = this.getSelectedNode();
21307         var a = [];
21308         if (!p) {
21309             a.push(p); // push blank onto stack..
21310             p = this.getParentElement();
21311         }
21312         
21313         
21314         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21315             a.push(p);
21316             p = p.parentNode;
21317         }
21318         a.push(this.doc.body);
21319         return a;
21320     },
21321     lastSel : false,
21322     lastSelNode : false,
21323     
21324     
21325     getSelection : function() 
21326     {
21327         this.assignDocWin();
21328         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21329     },
21330     
21331     getSelectedNode: function() 
21332     {
21333         // this may only work on Gecko!!!
21334         
21335         // should we cache this!!!!
21336         
21337         
21338         
21339          
21340         var range = this.createRange(this.getSelection()).cloneRange();
21341         
21342         if (Roo.isIE) {
21343             var parent = range.parentElement();
21344             while (true) {
21345                 var testRange = range.duplicate();
21346                 testRange.moveToElementText(parent);
21347                 if (testRange.inRange(range)) {
21348                     break;
21349                 }
21350                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21351                     break;
21352                 }
21353                 parent = parent.parentElement;
21354             }
21355             return parent;
21356         }
21357         
21358         // is ancestor a text element.
21359         var ac =  range.commonAncestorContainer;
21360         if (ac.nodeType == 3) {
21361             ac = ac.parentNode;
21362         }
21363         
21364         var ar = ac.childNodes;
21365          
21366         var nodes = [];
21367         var other_nodes = [];
21368         var has_other_nodes = false;
21369         for (var i=0;i<ar.length;i++) {
21370             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21371                 continue;
21372             }
21373             // fullly contained node.
21374             
21375             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21376                 nodes.push(ar[i]);
21377                 continue;
21378             }
21379             
21380             // probably selected..
21381             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21382                 other_nodes.push(ar[i]);
21383                 continue;
21384             }
21385             // outer..
21386             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21387                 continue;
21388             }
21389             
21390             
21391             has_other_nodes = true;
21392         }
21393         if (!nodes.length && other_nodes.length) {
21394             nodes= other_nodes;
21395         }
21396         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21397             return false;
21398         }
21399         
21400         return nodes[0];
21401     },
21402     createRange: function(sel)
21403     {
21404         // this has strange effects when using with 
21405         // top toolbar - not sure if it's a great idea.
21406         //this.editor.contentWindow.focus();
21407         if (typeof sel != "undefined") {
21408             try {
21409                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21410             } catch(e) {
21411                 return this.doc.createRange();
21412             }
21413         } else {
21414             return this.doc.createRange();
21415         }
21416     },
21417     getParentElement: function()
21418     {
21419         
21420         this.assignDocWin();
21421         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21422         
21423         var range = this.createRange(sel);
21424          
21425         try {
21426             var p = range.commonAncestorContainer;
21427             while (p.nodeType == 3) { // text node
21428                 p = p.parentNode;
21429             }
21430             return p;
21431         } catch (e) {
21432             return null;
21433         }
21434     
21435     },
21436     /***
21437      *
21438      * Range intersection.. the hard stuff...
21439      *  '-1' = before
21440      *  '0' = hits..
21441      *  '1' = after.
21442      *         [ -- selected range --- ]
21443      *   [fail]                        [fail]
21444      *
21445      *    basically..
21446      *      if end is before start or  hits it. fail.
21447      *      if start is after end or hits it fail.
21448      *
21449      *   if either hits (but other is outside. - then it's not 
21450      *   
21451      *    
21452      **/
21453     
21454     
21455     // @see http://www.thismuchiknow.co.uk/?p=64.
21456     rangeIntersectsNode : function(range, node)
21457     {
21458         var nodeRange = node.ownerDocument.createRange();
21459         try {
21460             nodeRange.selectNode(node);
21461         } catch (e) {
21462             nodeRange.selectNodeContents(node);
21463         }
21464     
21465         var rangeStartRange = range.cloneRange();
21466         rangeStartRange.collapse(true);
21467     
21468         var rangeEndRange = range.cloneRange();
21469         rangeEndRange.collapse(false);
21470     
21471         var nodeStartRange = nodeRange.cloneRange();
21472         nodeStartRange.collapse(true);
21473     
21474         var nodeEndRange = nodeRange.cloneRange();
21475         nodeEndRange.collapse(false);
21476     
21477         return rangeStartRange.compareBoundaryPoints(
21478                  Range.START_TO_START, nodeEndRange) == -1 &&
21479                rangeEndRange.compareBoundaryPoints(
21480                  Range.START_TO_START, nodeStartRange) == 1;
21481         
21482          
21483     },
21484     rangeCompareNode : function(range, node)
21485     {
21486         var nodeRange = node.ownerDocument.createRange();
21487         try {
21488             nodeRange.selectNode(node);
21489         } catch (e) {
21490             nodeRange.selectNodeContents(node);
21491         }
21492         
21493         
21494         range.collapse(true);
21495     
21496         nodeRange.collapse(true);
21497      
21498         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21499         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21500          
21501         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21502         
21503         var nodeIsBefore   =  ss == 1;
21504         var nodeIsAfter    = ee == -1;
21505         
21506         if (nodeIsBefore && nodeIsAfter) {
21507             return 0; // outer
21508         }
21509         if (!nodeIsBefore && nodeIsAfter) {
21510             return 1; //right trailed.
21511         }
21512         
21513         if (nodeIsBefore && !nodeIsAfter) {
21514             return 2;  // left trailed.
21515         }
21516         // fully contined.
21517         return 3;
21518     },
21519
21520     // private? - in a new class?
21521     cleanUpPaste :  function()
21522     {
21523         // cleans up the whole document..
21524         Roo.log('cleanuppaste');
21525         
21526         this.cleanUpChildren(this.doc.body);
21527         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21528         if (clean != this.doc.body.innerHTML) {
21529             this.doc.body.innerHTML = clean;
21530         }
21531         
21532     },
21533     
21534     cleanWordChars : function(input) {// change the chars to hex code
21535         var he = Roo.HtmlEditorCore;
21536         
21537         var output = input;
21538         Roo.each(he.swapCodes, function(sw) { 
21539             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21540             
21541             output = output.replace(swapper, sw[1]);
21542         });
21543         
21544         return output;
21545     },
21546     
21547     
21548     cleanUpChildren : function (n)
21549     {
21550         if (!n.childNodes.length) {
21551             return;
21552         }
21553         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21554            this.cleanUpChild(n.childNodes[i]);
21555         }
21556     },
21557     
21558     
21559         
21560     
21561     cleanUpChild : function (node)
21562     {
21563         var ed = this;
21564         //console.log(node);
21565         if (node.nodeName == "#text") {
21566             // clean up silly Windows -- stuff?
21567             return; 
21568         }
21569         if (node.nodeName == "#comment") {
21570             if (!this.allowComments) {
21571                 node.parentNode.removeChild(node);
21572             }
21573             // clean up silly Windows -- stuff?
21574             return; 
21575         }
21576         var lcname = node.tagName.toLowerCase();
21577         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21578         // whitelist of tags..
21579         
21580         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21581             // remove node.
21582             node.parentNode.removeChild(node);
21583             return;
21584             
21585         }
21586         
21587         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21588         
21589         // spans with no attributes - just remove them..
21590         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
21591             remove_keep_children = true;
21592         }
21593         
21594         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21595         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21596         
21597         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21598         //    remove_keep_children = true;
21599         //}
21600         
21601         if (remove_keep_children) {
21602             this.cleanUpChildren(node);
21603             // inserts everything just before this node...
21604             while (node.childNodes.length) {
21605                 var cn = node.childNodes[0];
21606                 node.removeChild(cn);
21607                 node.parentNode.insertBefore(cn, node);
21608             }
21609             node.parentNode.removeChild(node);
21610             return;
21611         }
21612         
21613         if (!node.attributes || !node.attributes.length) {
21614             
21615           
21616             
21617             
21618             this.cleanUpChildren(node);
21619             return;
21620         }
21621         
21622         function cleanAttr(n,v)
21623         {
21624             
21625             if (v.match(/^\./) || v.match(/^\//)) {
21626                 return;
21627             }
21628             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21629                 return;
21630             }
21631             if (v.match(/^#/)) {
21632                 return;
21633             }
21634             if (v.match(/^\{/)) { // allow template editing.
21635                 return;
21636             }
21637 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21638             node.removeAttribute(n);
21639             
21640         }
21641         
21642         var cwhite = this.cwhite;
21643         var cblack = this.cblack;
21644             
21645         function cleanStyle(n,v)
21646         {
21647             if (v.match(/expression/)) { //XSS?? should we even bother..
21648                 node.removeAttribute(n);
21649                 return;
21650             }
21651             
21652             var parts = v.split(/;/);
21653             var clean = [];
21654             
21655             Roo.each(parts, function(p) {
21656                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21657                 if (!p.length) {
21658                     return true;
21659                 }
21660                 var l = p.split(':').shift().replace(/\s+/g,'');
21661                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21662                 
21663                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21664 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21665                     //node.removeAttribute(n);
21666                     return true;
21667                 }
21668                 //Roo.log()
21669                 // only allow 'c whitelisted system attributes'
21670                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21671 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21672                     //node.removeAttribute(n);
21673                     return true;
21674                 }
21675                 
21676                 
21677                  
21678                 
21679                 clean.push(p);
21680                 return true;
21681             });
21682             if (clean.length) { 
21683                 node.setAttribute(n, clean.join(';'));
21684             } else {
21685                 node.removeAttribute(n);
21686             }
21687             
21688         }
21689         
21690         
21691         for (var i = node.attributes.length-1; i > -1 ; i--) {
21692             var a = node.attributes[i];
21693             //console.log(a);
21694             
21695             if (a.name.toLowerCase().substr(0,2)=='on')  {
21696                 node.removeAttribute(a.name);
21697                 continue;
21698             }
21699             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21700                 node.removeAttribute(a.name);
21701                 continue;
21702             }
21703             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21704                 cleanAttr(a.name,a.value); // fixme..
21705                 continue;
21706             }
21707             if (a.name == 'style') {
21708                 cleanStyle(a.name,a.value);
21709                 continue;
21710             }
21711             /// clean up MS crap..
21712             // tecnically this should be a list of valid class'es..
21713             
21714             
21715             if (a.name == 'class') {
21716                 if (a.value.match(/^Mso/)) {
21717                     node.removeAttribute('class');
21718                 }
21719                 
21720                 if (a.value.match(/^body$/)) {
21721                     node.removeAttribute('class');
21722                 }
21723                 continue;
21724             }
21725             
21726             // style cleanup!?
21727             // class cleanup?
21728             
21729         }
21730         
21731         
21732         this.cleanUpChildren(node);
21733         
21734         
21735     },
21736     
21737     /**
21738      * Clean up MS wordisms...
21739      */
21740     cleanWord : function(node)
21741     {
21742         if (!node) {
21743             this.cleanWord(this.doc.body);
21744             return;
21745         }
21746         
21747         if(
21748                 node.nodeName == 'SPAN' &&
21749                 !node.hasAttributes() &&
21750                 node.childNodes.length == 1 &&
21751                 node.firstChild.nodeName == "#text"  
21752         ) {
21753             var textNode = node.firstChild;
21754             node.removeChild(textNode);
21755             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21756                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
21757             }
21758             node.parentNode.insertBefore(textNode, node);
21759             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21760                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
21761             }
21762             node.parentNode.removeChild(node);
21763         }
21764         
21765         if (node.nodeName == "#text") {
21766             // clean up silly Windows -- stuff?
21767             return; 
21768         }
21769         if (node.nodeName == "#comment") {
21770             node.parentNode.removeChild(node);
21771             // clean up silly Windows -- stuff?
21772             return; 
21773         }
21774         
21775         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21776             node.parentNode.removeChild(node);
21777             return;
21778         }
21779         //Roo.log(node.tagName);
21780         // remove - but keep children..
21781         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
21782             //Roo.log('-- removed');
21783             while (node.childNodes.length) {
21784                 var cn = node.childNodes[0];
21785                 node.removeChild(cn);
21786                 node.parentNode.insertBefore(cn, node);
21787                 // move node to parent - and clean it..
21788                 this.cleanWord(cn);
21789             }
21790             node.parentNode.removeChild(node);
21791             /// no need to iterate chidlren = it's got none..
21792             //this.iterateChildren(node, this.cleanWord);
21793             return;
21794         }
21795         // clean styles
21796         if (node.className.length) {
21797             
21798             var cn = node.className.split(/\W+/);
21799             var cna = [];
21800             Roo.each(cn, function(cls) {
21801                 if (cls.match(/Mso[a-zA-Z]+/)) {
21802                     return;
21803                 }
21804                 cna.push(cls);
21805             });
21806             node.className = cna.length ? cna.join(' ') : '';
21807             if (!cna.length) {
21808                 node.removeAttribute("class");
21809             }
21810         }
21811         
21812         if (node.hasAttribute("lang")) {
21813             node.removeAttribute("lang");
21814         }
21815         
21816         if (node.hasAttribute("style")) {
21817             
21818             var styles = node.getAttribute("style").split(";");
21819             var nstyle = [];
21820             Roo.each(styles, function(s) {
21821                 if (!s.match(/:/)) {
21822                     return;
21823                 }
21824                 var kv = s.split(":");
21825                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21826                     return;
21827                 }
21828                 // what ever is left... we allow.
21829                 nstyle.push(s);
21830             });
21831             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21832             if (!nstyle.length) {
21833                 node.removeAttribute('style');
21834             }
21835         }
21836         this.iterateChildren(node, this.cleanWord);
21837         
21838         
21839         
21840     },
21841     /**
21842      * iterateChildren of a Node, calling fn each time, using this as the scole..
21843      * @param {DomNode} node node to iterate children of.
21844      * @param {Function} fn method of this class to call on each item.
21845      */
21846     iterateChildren : function(node, fn)
21847     {
21848         if (!node.childNodes.length) {
21849                 return;
21850         }
21851         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21852            fn.call(this, node.childNodes[i])
21853         }
21854     },
21855     
21856     
21857     /**
21858      * cleanTableWidths.
21859      *
21860      * Quite often pasting from word etc.. results in tables with column and widths.
21861      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21862      *
21863      */
21864     cleanTableWidths : function(node)
21865     {
21866          
21867          
21868         if (!node) {
21869             this.cleanTableWidths(this.doc.body);
21870             return;
21871         }
21872         
21873         // ignore list...
21874         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21875             return; 
21876         }
21877         Roo.log(node.tagName);
21878         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21879             this.iterateChildren(node, this.cleanTableWidths);
21880             return;
21881         }
21882         if (node.hasAttribute('width')) {
21883             node.removeAttribute('width');
21884         }
21885         
21886          
21887         if (node.hasAttribute("style")) {
21888             // pretty basic...
21889             
21890             var styles = node.getAttribute("style").split(";");
21891             var nstyle = [];
21892             Roo.each(styles, function(s) {
21893                 if (!s.match(/:/)) {
21894                     return;
21895                 }
21896                 var kv = s.split(":");
21897                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21898                     return;
21899                 }
21900                 // what ever is left... we allow.
21901                 nstyle.push(s);
21902             });
21903             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21904             if (!nstyle.length) {
21905                 node.removeAttribute('style');
21906             }
21907         }
21908         
21909         this.iterateChildren(node, this.cleanTableWidths);
21910         
21911         
21912     },
21913     
21914     
21915     
21916     
21917     domToHTML : function(currentElement, depth, nopadtext) {
21918         
21919         depth = depth || 0;
21920         nopadtext = nopadtext || false;
21921     
21922         if (!currentElement) {
21923             return this.domToHTML(this.doc.body);
21924         }
21925         
21926         //Roo.log(currentElement);
21927         var j;
21928         var allText = false;
21929         var nodeName = currentElement.nodeName;
21930         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21931         
21932         if  (nodeName == '#text') {
21933             
21934             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21935         }
21936         
21937         
21938         var ret = '';
21939         if (nodeName != 'BODY') {
21940              
21941             var i = 0;
21942             // Prints the node tagName, such as <A>, <IMG>, etc
21943             if (tagName) {
21944                 var attr = [];
21945                 for(i = 0; i < currentElement.attributes.length;i++) {
21946                     // quoting?
21947                     var aname = currentElement.attributes.item(i).name;
21948                     if (!currentElement.attributes.item(i).value.length) {
21949                         continue;
21950                     }
21951                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21952                 }
21953                 
21954                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21955             } 
21956             else {
21957                 
21958                 // eack
21959             }
21960         } else {
21961             tagName = false;
21962         }
21963         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21964             return ret;
21965         }
21966         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21967             nopadtext = true;
21968         }
21969         
21970         
21971         // Traverse the tree
21972         i = 0;
21973         var currentElementChild = currentElement.childNodes.item(i);
21974         var allText = true;
21975         var innerHTML  = '';
21976         lastnode = '';
21977         while (currentElementChild) {
21978             // Formatting code (indent the tree so it looks nice on the screen)
21979             var nopad = nopadtext;
21980             if (lastnode == 'SPAN') {
21981                 nopad  = true;
21982             }
21983             // text
21984             if  (currentElementChild.nodeName == '#text') {
21985                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21986                 toadd = nopadtext ? toadd : toadd.trim();
21987                 if (!nopad && toadd.length > 80) {
21988                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21989                 }
21990                 innerHTML  += toadd;
21991                 
21992                 i++;
21993                 currentElementChild = currentElement.childNodes.item(i);
21994                 lastNode = '';
21995                 continue;
21996             }
21997             allText = false;
21998             
21999             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22000                 
22001             // Recursively traverse the tree structure of the child node
22002             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22003             lastnode = currentElementChild.nodeName;
22004             i++;
22005             currentElementChild=currentElement.childNodes.item(i);
22006         }
22007         
22008         ret += innerHTML;
22009         
22010         if (!allText) {
22011                 // The remaining code is mostly for formatting the tree
22012             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22013         }
22014         
22015         
22016         if (tagName) {
22017             ret+= "</"+tagName+">";
22018         }
22019         return ret;
22020         
22021     },
22022         
22023     applyBlacklists : function()
22024     {
22025         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22026         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22027         
22028         this.white = [];
22029         this.black = [];
22030         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22031             if (b.indexOf(tag) > -1) {
22032                 return;
22033             }
22034             this.white.push(tag);
22035             
22036         }, this);
22037         
22038         Roo.each(w, function(tag) {
22039             if (b.indexOf(tag) > -1) {
22040                 return;
22041             }
22042             if (this.white.indexOf(tag) > -1) {
22043                 return;
22044             }
22045             this.white.push(tag);
22046             
22047         }, this);
22048         
22049         
22050         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22051             if (w.indexOf(tag) > -1) {
22052                 return;
22053             }
22054             this.black.push(tag);
22055             
22056         }, this);
22057         
22058         Roo.each(b, function(tag) {
22059             if (w.indexOf(tag) > -1) {
22060                 return;
22061             }
22062             if (this.black.indexOf(tag) > -1) {
22063                 return;
22064             }
22065             this.black.push(tag);
22066             
22067         }, this);
22068         
22069         
22070         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22071         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22072         
22073         this.cwhite = [];
22074         this.cblack = [];
22075         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22076             if (b.indexOf(tag) > -1) {
22077                 return;
22078             }
22079             this.cwhite.push(tag);
22080             
22081         }, this);
22082         
22083         Roo.each(w, function(tag) {
22084             if (b.indexOf(tag) > -1) {
22085                 return;
22086             }
22087             if (this.cwhite.indexOf(tag) > -1) {
22088                 return;
22089             }
22090             this.cwhite.push(tag);
22091             
22092         }, this);
22093         
22094         
22095         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22096             if (w.indexOf(tag) > -1) {
22097                 return;
22098             }
22099             this.cblack.push(tag);
22100             
22101         }, this);
22102         
22103         Roo.each(b, function(tag) {
22104             if (w.indexOf(tag) > -1) {
22105                 return;
22106             }
22107             if (this.cblack.indexOf(tag) > -1) {
22108                 return;
22109             }
22110             this.cblack.push(tag);
22111             
22112         }, this);
22113     },
22114     
22115     setStylesheets : function(stylesheets)
22116     {
22117         if(typeof(stylesheets) == 'string'){
22118             Roo.get(this.iframe.contentDocument.head).createChild({
22119                 tag : 'link',
22120                 rel : 'stylesheet',
22121                 type : 'text/css',
22122                 href : stylesheets
22123             });
22124             
22125             return;
22126         }
22127         var _this = this;
22128      
22129         Roo.each(stylesheets, function(s) {
22130             if(!s.length){
22131                 return;
22132             }
22133             
22134             Roo.get(_this.iframe.contentDocument.head).createChild({
22135                 tag : 'link',
22136                 rel : 'stylesheet',
22137                 type : 'text/css',
22138                 href : s
22139             });
22140         });
22141
22142         
22143     },
22144     
22145     removeStylesheets : function()
22146     {
22147         var _this = this;
22148         
22149         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22150             s.remove();
22151         });
22152     },
22153     
22154     setStyle : function(style)
22155     {
22156         Roo.get(this.iframe.contentDocument.head).createChild({
22157             tag : 'style',
22158             type : 'text/css',
22159             html : style
22160         });
22161
22162         return;
22163     }
22164     
22165     // hide stuff that is not compatible
22166     /**
22167      * @event blur
22168      * @hide
22169      */
22170     /**
22171      * @event change
22172      * @hide
22173      */
22174     /**
22175      * @event focus
22176      * @hide
22177      */
22178     /**
22179      * @event specialkey
22180      * @hide
22181      */
22182     /**
22183      * @cfg {String} fieldClass @hide
22184      */
22185     /**
22186      * @cfg {String} focusClass @hide
22187      */
22188     /**
22189      * @cfg {String} autoCreate @hide
22190      */
22191     /**
22192      * @cfg {String} inputType @hide
22193      */
22194     /**
22195      * @cfg {String} invalidClass @hide
22196      */
22197     /**
22198      * @cfg {String} invalidText @hide
22199      */
22200     /**
22201      * @cfg {String} msgFx @hide
22202      */
22203     /**
22204      * @cfg {String} validateOnBlur @hide
22205      */
22206 });
22207
22208 Roo.HtmlEditorCore.white = [
22209         'area', 'br', 'img', 'input', 'hr', 'wbr',
22210         
22211        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22212        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22213        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22214        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22215        'table',   'ul',         'xmp', 
22216        
22217        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22218       'thead',   'tr', 
22219      
22220       'dir', 'menu', 'ol', 'ul', 'dl',
22221        
22222       'embed',  'object'
22223 ];
22224
22225
22226 Roo.HtmlEditorCore.black = [
22227     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22228         'applet', // 
22229         'base',   'basefont', 'bgsound', 'blink',  'body', 
22230         'frame',  'frameset', 'head',    'html',   'ilayer', 
22231         'iframe', 'layer',  'link',     'meta',    'object',   
22232         'script', 'style' ,'title',  'xml' // clean later..
22233 ];
22234 Roo.HtmlEditorCore.clean = [
22235     'script', 'style', 'title', 'xml'
22236 ];
22237 Roo.HtmlEditorCore.remove = [
22238     'font'
22239 ];
22240 // attributes..
22241
22242 Roo.HtmlEditorCore.ablack = [
22243     'on'
22244 ];
22245     
22246 Roo.HtmlEditorCore.aclean = [ 
22247     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22248 ];
22249
22250 // protocols..
22251 Roo.HtmlEditorCore.pwhite= [
22252         'http',  'https',  'mailto'
22253 ];
22254
22255 // white listed style attributes.
22256 Roo.HtmlEditorCore.cwhite= [
22257       //  'text-align', /// default is to allow most things..
22258       
22259          
22260 //        'font-size'//??
22261 ];
22262
22263 // black listed style attributes.
22264 Roo.HtmlEditorCore.cblack= [
22265       //  'font-size' -- this can be set by the project 
22266 ];
22267
22268
22269 Roo.HtmlEditorCore.swapCodes   =[ 
22270     [    8211, "&#8211;" ], 
22271     [    8212, "&#8212;" ], 
22272     [    8216,  "'" ],  
22273     [    8217, "'" ],  
22274     [    8220, '"' ],  
22275     [    8221, '"' ],  
22276     [    8226, "*" ],  
22277     [    8230, "..." ]
22278 ]; 
22279
22280     //<script type="text/javascript">
22281
22282 /*
22283  * Ext JS Library 1.1.1
22284  * Copyright(c) 2006-2007, Ext JS, LLC.
22285  * Licence LGPL
22286  * 
22287  */
22288  
22289  
22290 Roo.form.HtmlEditor = function(config){
22291     
22292     
22293     
22294     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22295     
22296     if (!this.toolbars) {
22297         this.toolbars = [];
22298     }
22299     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22300     
22301     
22302 };
22303
22304 /**
22305  * @class Roo.form.HtmlEditor
22306  * @extends Roo.form.Field
22307  * Provides a lightweight HTML Editor component.
22308  *
22309  * This has been tested on Fireforx / Chrome.. IE may not be so great..
22310  * 
22311  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22312  * supported by this editor.</b><br/><br/>
22313  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22314  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22315  */
22316 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22317     /**
22318      * @cfg {Boolean} clearUp
22319      */
22320     clearUp : true,
22321       /**
22322      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22323      */
22324     toolbars : false,
22325    
22326      /**
22327      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22328      *                        Roo.resizable.
22329      */
22330     resizable : false,
22331      /**
22332      * @cfg {Number} height (in pixels)
22333      */   
22334     height: 300,
22335    /**
22336      * @cfg {Number} width (in pixels)
22337      */   
22338     width: 500,
22339     
22340     /**
22341      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22342      * 
22343      */
22344     stylesheets: false,
22345     
22346     
22347      /**
22348      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22349      * 
22350      */
22351     cblack: false,
22352     /**
22353      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22354      * 
22355      */
22356     cwhite: false,
22357     
22358      /**
22359      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22360      * 
22361      */
22362     black: false,
22363     /**
22364      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22365      * 
22366      */
22367     white: false,
22368     /**
22369      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
22370      */
22371     allowComments: false,
22372     
22373     // id of frame..
22374     frameId: false,
22375     
22376     // private properties
22377     validationEvent : false,
22378     deferHeight: true,
22379     initialized : false,
22380     activated : false,
22381     
22382     onFocus : Roo.emptyFn,
22383     iframePad:3,
22384     hideMode:'offsets',
22385     
22386     actionMode : 'container', // defaults to hiding it...
22387     
22388     defaultAutoCreate : { // modified by initCompnoent..
22389         tag: "textarea",
22390         style:"width:500px;height:300px;",
22391         autocomplete: "new-password"
22392     },
22393
22394     // private
22395     initComponent : function(){
22396         this.addEvents({
22397             /**
22398              * @event initialize
22399              * Fires when the editor is fully initialized (including the iframe)
22400              * @param {HtmlEditor} this
22401              */
22402             initialize: true,
22403             /**
22404              * @event activate
22405              * Fires when the editor is first receives the focus. Any insertion must wait
22406              * until after this event.
22407              * @param {HtmlEditor} this
22408              */
22409             activate: true,
22410              /**
22411              * @event beforesync
22412              * Fires before the textarea is updated with content from the editor iframe. Return false
22413              * to cancel the sync.
22414              * @param {HtmlEditor} this
22415              * @param {String} html
22416              */
22417             beforesync: true,
22418              /**
22419              * @event beforepush
22420              * Fires before the iframe editor is updated with content from the textarea. Return false
22421              * to cancel the push.
22422              * @param {HtmlEditor} this
22423              * @param {String} html
22424              */
22425             beforepush: true,
22426              /**
22427              * @event sync
22428              * Fires when the textarea is updated with content from the editor iframe.
22429              * @param {HtmlEditor} this
22430              * @param {String} html
22431              */
22432             sync: true,
22433              /**
22434              * @event push
22435              * Fires when the iframe editor is updated with content from the textarea.
22436              * @param {HtmlEditor} this
22437              * @param {String} html
22438              */
22439             push: true,
22440              /**
22441              * @event editmodechange
22442              * Fires when the editor switches edit modes
22443              * @param {HtmlEditor} this
22444              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22445              */
22446             editmodechange: true,
22447             /**
22448              * @event editorevent
22449              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22450              * @param {HtmlEditor} this
22451              */
22452             editorevent: true,
22453             /**
22454              * @event firstfocus
22455              * Fires when on first focus - needed by toolbars..
22456              * @param {HtmlEditor} this
22457              */
22458             firstfocus: true,
22459             /**
22460              * @event autosave
22461              * Auto save the htmlEditor value as a file into Events
22462              * @param {HtmlEditor} this
22463              */
22464             autosave: true,
22465             /**
22466              * @event savedpreview
22467              * preview the saved version of htmlEditor
22468              * @param {HtmlEditor} this
22469              */
22470             savedpreview: true,
22471             
22472             /**
22473             * @event stylesheetsclick
22474             * Fires when press the Sytlesheets button
22475             * @param {Roo.HtmlEditorCore} this
22476             */
22477             stylesheetsclick: true
22478         });
22479         this.defaultAutoCreate =  {
22480             tag: "textarea",
22481             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22482             autocomplete: "new-password"
22483         };
22484     },
22485
22486     /**
22487      * Protected method that will not generally be called directly. It
22488      * is called when the editor creates its toolbar. Override this method if you need to
22489      * add custom toolbar buttons.
22490      * @param {HtmlEditor} editor
22491      */
22492     createToolbar : function(editor){
22493         Roo.log("create toolbars");
22494         if (!editor.toolbars || !editor.toolbars.length) {
22495             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22496         }
22497         
22498         for (var i =0 ; i < editor.toolbars.length;i++) {
22499             editor.toolbars[i] = Roo.factory(
22500                     typeof(editor.toolbars[i]) == 'string' ?
22501                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
22502                 Roo.form.HtmlEditor);
22503             editor.toolbars[i].init(editor);
22504         }
22505          
22506         
22507     },
22508
22509      
22510     // private
22511     onRender : function(ct, position)
22512     {
22513         var _t = this;
22514         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22515         
22516         this.wrap = this.el.wrap({
22517             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22518         });
22519         
22520         this.editorcore.onRender(ct, position);
22521          
22522         if (this.resizable) {
22523             this.resizeEl = new Roo.Resizable(this.wrap, {
22524                 pinned : true,
22525                 wrap: true,
22526                 dynamic : true,
22527                 minHeight : this.height,
22528                 height: this.height,
22529                 handles : this.resizable,
22530                 width: this.width,
22531                 listeners : {
22532                     resize : function(r, w, h) {
22533                         _t.onResize(w,h); // -something
22534                     }
22535                 }
22536             });
22537             
22538         }
22539         this.createToolbar(this);
22540        
22541         
22542         if(!this.width){
22543             this.setSize(this.wrap.getSize());
22544         }
22545         if (this.resizeEl) {
22546             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22547             // should trigger onReize..
22548         }
22549         
22550         this.keyNav = new Roo.KeyNav(this.el, {
22551             
22552             "tab" : function(e){
22553                 e.preventDefault();
22554                 
22555                 var value = this.getValue();
22556                 
22557                 var start = this.el.dom.selectionStart;
22558                 var end = this.el.dom.selectionEnd;
22559                 
22560                 if(!e.shiftKey){
22561                     
22562                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22563                     this.el.dom.setSelectionRange(end + 1, end + 1);
22564                     return;
22565                 }
22566                 
22567                 var f = value.substring(0, start).split("\t");
22568                 
22569                 if(f.pop().length != 0){
22570                     return;
22571                 }
22572                 
22573                 this.setValue(f.join("\t") + value.substring(end));
22574                 this.el.dom.setSelectionRange(start - 1, start - 1);
22575                 
22576             },
22577             
22578             "home" : function(e){
22579                 e.preventDefault();
22580                 
22581                 var curr = this.el.dom.selectionStart;
22582                 var lines = this.getValue().split("\n");
22583                 
22584                 if(!lines.length){
22585                     return;
22586                 }
22587                 
22588                 if(e.ctrlKey){
22589                     this.el.dom.setSelectionRange(0, 0);
22590                     return;
22591                 }
22592                 
22593                 var pos = 0;
22594                 
22595                 for (var i = 0; i < lines.length;i++) {
22596                     pos += lines[i].length;
22597                     
22598                     if(i != 0){
22599                         pos += 1;
22600                     }
22601                     
22602                     if(pos < curr){
22603                         continue;
22604                     }
22605                     
22606                     pos -= lines[i].length;
22607                     
22608                     break;
22609                 }
22610                 
22611                 if(!e.shiftKey){
22612                     this.el.dom.setSelectionRange(pos, pos);
22613                     return;
22614                 }
22615                 
22616                 this.el.dom.selectionStart = pos;
22617                 this.el.dom.selectionEnd = curr;
22618             },
22619             
22620             "end" : function(e){
22621                 e.preventDefault();
22622                 
22623                 var curr = this.el.dom.selectionStart;
22624                 var lines = this.getValue().split("\n");
22625                 
22626                 if(!lines.length){
22627                     return;
22628                 }
22629                 
22630                 if(e.ctrlKey){
22631                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22632                     return;
22633                 }
22634                 
22635                 var pos = 0;
22636                 
22637                 for (var i = 0; i < lines.length;i++) {
22638                     
22639                     pos += lines[i].length;
22640                     
22641                     if(i != 0){
22642                         pos += 1;
22643                     }
22644                     
22645                     if(pos < curr){
22646                         continue;
22647                     }
22648                     
22649                     break;
22650                 }
22651                 
22652                 if(!e.shiftKey){
22653                     this.el.dom.setSelectionRange(pos, pos);
22654                     return;
22655                 }
22656                 
22657                 this.el.dom.selectionStart = curr;
22658                 this.el.dom.selectionEnd = pos;
22659             },
22660
22661             scope : this,
22662
22663             doRelay : function(foo, bar, hname){
22664                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22665             },
22666
22667             forceKeyDown: true
22668         });
22669         
22670 //        if(this.autosave && this.w){
22671 //            this.autoSaveFn = setInterval(this.autosave, 1000);
22672 //        }
22673     },
22674
22675     // private
22676     onResize : function(w, h)
22677     {
22678         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22679         var ew = false;
22680         var eh = false;
22681         
22682         if(this.el ){
22683             if(typeof w == 'number'){
22684                 var aw = w - this.wrap.getFrameWidth('lr');
22685                 this.el.setWidth(this.adjustWidth('textarea', aw));
22686                 ew = aw;
22687             }
22688             if(typeof h == 'number'){
22689                 var tbh = 0;
22690                 for (var i =0; i < this.toolbars.length;i++) {
22691                     // fixme - ask toolbars for heights?
22692                     tbh += this.toolbars[i].tb.el.getHeight();
22693                     if (this.toolbars[i].footer) {
22694                         tbh += this.toolbars[i].footer.el.getHeight();
22695                     }
22696                 }
22697                 
22698                 
22699                 
22700                 
22701                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22702                 ah -= 5; // knock a few pixes off for look..
22703 //                Roo.log(ah);
22704                 this.el.setHeight(this.adjustWidth('textarea', ah));
22705                 var eh = ah;
22706             }
22707         }
22708         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22709         this.editorcore.onResize(ew,eh);
22710         
22711     },
22712
22713     /**
22714      * Toggles the editor between standard and source edit mode.
22715      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22716      */
22717     toggleSourceEdit : function(sourceEditMode)
22718     {
22719         this.editorcore.toggleSourceEdit(sourceEditMode);
22720         
22721         if(this.editorcore.sourceEditMode){
22722             Roo.log('editor - showing textarea');
22723             
22724 //            Roo.log('in');
22725 //            Roo.log(this.syncValue());
22726             this.editorcore.syncValue();
22727             this.el.removeClass('x-hidden');
22728             this.el.dom.removeAttribute('tabIndex');
22729             this.el.focus();
22730             
22731             for (var i = 0; i < this.toolbars.length; i++) {
22732                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22733                     this.toolbars[i].tb.hide();
22734                     this.toolbars[i].footer.hide();
22735                 }
22736             }
22737             
22738         }else{
22739             Roo.log('editor - hiding textarea');
22740 //            Roo.log('out')
22741 //            Roo.log(this.pushValue()); 
22742             this.editorcore.pushValue();
22743             
22744             this.el.addClass('x-hidden');
22745             this.el.dom.setAttribute('tabIndex', -1);
22746             
22747             for (var i = 0; i < this.toolbars.length; i++) {
22748                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22749                     this.toolbars[i].tb.show();
22750                     this.toolbars[i].footer.show();
22751                 }
22752             }
22753             
22754             //this.deferFocus();
22755         }
22756         
22757         this.setSize(this.wrap.getSize());
22758         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
22759         
22760         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22761     },
22762  
22763     // private (for BoxComponent)
22764     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22765
22766     // private (for BoxComponent)
22767     getResizeEl : function(){
22768         return this.wrap;
22769     },
22770
22771     // private (for BoxComponent)
22772     getPositionEl : function(){
22773         return this.wrap;
22774     },
22775
22776     // private
22777     initEvents : function(){
22778         this.originalValue = this.getValue();
22779     },
22780
22781     /**
22782      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22783      * @method
22784      */
22785     markInvalid : Roo.emptyFn,
22786     /**
22787      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22788      * @method
22789      */
22790     clearInvalid : Roo.emptyFn,
22791
22792     setValue : function(v){
22793         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
22794         this.editorcore.pushValue();
22795     },
22796
22797      
22798     // private
22799     deferFocus : function(){
22800         this.focus.defer(10, this);
22801     },
22802
22803     // doc'ed in Field
22804     focus : function(){
22805         this.editorcore.focus();
22806         
22807     },
22808       
22809
22810     // private
22811     onDestroy : function(){
22812         
22813         
22814         
22815         if(this.rendered){
22816             
22817             for (var i =0; i < this.toolbars.length;i++) {
22818                 // fixme - ask toolbars for heights?
22819                 this.toolbars[i].onDestroy();
22820             }
22821             
22822             this.wrap.dom.innerHTML = '';
22823             this.wrap.remove();
22824         }
22825     },
22826
22827     // private
22828     onFirstFocus : function(){
22829         //Roo.log("onFirstFocus");
22830         this.editorcore.onFirstFocus();
22831          for (var i =0; i < this.toolbars.length;i++) {
22832             this.toolbars[i].onFirstFocus();
22833         }
22834         
22835     },
22836     
22837     // private
22838     syncValue : function()
22839     {
22840         this.editorcore.syncValue();
22841     },
22842     
22843     pushValue : function()
22844     {
22845         this.editorcore.pushValue();
22846     },
22847     
22848     setStylesheets : function(stylesheets)
22849     {
22850         this.editorcore.setStylesheets(stylesheets);
22851     },
22852     
22853     removeStylesheets : function()
22854     {
22855         this.editorcore.removeStylesheets();
22856     }
22857      
22858     
22859     // hide stuff that is not compatible
22860     /**
22861      * @event blur
22862      * @hide
22863      */
22864     /**
22865      * @event change
22866      * @hide
22867      */
22868     /**
22869      * @event focus
22870      * @hide
22871      */
22872     /**
22873      * @event specialkey
22874      * @hide
22875      */
22876     /**
22877      * @cfg {String} fieldClass @hide
22878      */
22879     /**
22880      * @cfg {String} focusClass @hide
22881      */
22882     /**
22883      * @cfg {String} autoCreate @hide
22884      */
22885     /**
22886      * @cfg {String} inputType @hide
22887      */
22888     /**
22889      * @cfg {String} invalidClass @hide
22890      */
22891     /**
22892      * @cfg {String} invalidText @hide
22893      */
22894     /**
22895      * @cfg {String} msgFx @hide
22896      */
22897     /**
22898      * @cfg {String} validateOnBlur @hide
22899      */
22900 });
22901  
22902     // <script type="text/javascript">
22903 /*
22904  * Based on
22905  * Ext JS Library 1.1.1
22906  * Copyright(c) 2006-2007, Ext JS, LLC.
22907  *  
22908  
22909  */
22910
22911 /**
22912  * @class Roo.form.HtmlEditorToolbar1
22913  * Basic Toolbar
22914  * 
22915  * Usage:
22916  *
22917  new Roo.form.HtmlEditor({
22918     ....
22919     toolbars : [
22920         new Roo.form.HtmlEditorToolbar1({
22921             disable : { fonts: 1 , format: 1, ..., ... , ...],
22922             btns : [ .... ]
22923         })
22924     }
22925      
22926  * 
22927  * @cfg {Object} disable List of elements to disable..
22928  * @cfg {Array} btns List of additional buttons.
22929  * 
22930  * 
22931  * NEEDS Extra CSS? 
22932  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22933  */
22934  
22935 Roo.form.HtmlEditor.ToolbarStandard = function(config)
22936 {
22937     
22938     Roo.apply(this, config);
22939     
22940     // default disabled, based on 'good practice'..
22941     this.disable = this.disable || {};
22942     Roo.applyIf(this.disable, {
22943         fontSize : true,
22944         colors : true,
22945         specialElements : true
22946     });
22947     
22948     
22949     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22950     // dont call parent... till later.
22951 }
22952
22953 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
22954     
22955     tb: false,
22956     
22957     rendered: false,
22958     
22959     editor : false,
22960     editorcore : false,
22961     /**
22962      * @cfg {Object} disable  List of toolbar elements to disable
22963          
22964      */
22965     disable : false,
22966     
22967     
22968      /**
22969      * @cfg {String} createLinkText The default text for the create link prompt
22970      */
22971     createLinkText : 'Please enter the URL for the link:',
22972     /**
22973      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
22974      */
22975     defaultLinkValue : 'http:/'+'/',
22976    
22977     
22978       /**
22979      * @cfg {Array} fontFamilies An array of available font families
22980      */
22981     fontFamilies : [
22982         'Arial',
22983         'Courier New',
22984         'Tahoma',
22985         'Times New Roman',
22986         'Verdana'
22987     ],
22988     
22989     specialChars : [
22990            "&#169;",
22991           "&#174;",     
22992           "&#8482;",    
22993           "&#163;" ,    
22994          // "&#8212;",    
22995           "&#8230;",    
22996           "&#247;" ,    
22997         //  "&#225;" ,     ?? a acute?
22998            "&#8364;"    , //Euro
22999        //   "&#8220;"    ,
23000         //  "&#8221;"    ,
23001         //  "&#8226;"    ,
23002           "&#176;"  //   , // degrees
23003
23004          // "&#233;"     , // e ecute
23005          // "&#250;"     , // u ecute?
23006     ],
23007     
23008     specialElements : [
23009         {
23010             text: "Insert Table",
23011             xtype: 'MenuItem',
23012             xns : Roo.Menu,
23013             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
23014                 
23015         },
23016         {    
23017             text: "Insert Image",
23018             xtype: 'MenuItem',
23019             xns : Roo.Menu,
23020             ihtml : '<img src="about:blank"/>'
23021             
23022         }
23023         
23024          
23025     ],
23026     
23027     
23028     inputElements : [ 
23029             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
23030             "input:submit", "input:button", "select", "textarea", "label" ],
23031     formats : [
23032         ["p"] ,  
23033         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
23034         ["pre"],[ "code"], 
23035         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
23036         ['div'],['span'],
23037         ['sup'],['sub']
23038     ],
23039     
23040     cleanStyles : [
23041         "font-size"
23042     ],
23043      /**
23044      * @cfg {String} defaultFont default font to use.
23045      */
23046     defaultFont: 'tahoma',
23047    
23048     fontSelect : false,
23049     
23050     
23051     formatCombo : false,
23052     
23053     init : function(editor)
23054     {
23055         this.editor = editor;
23056         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23057         var editorcore = this.editorcore;
23058         
23059         var _t = this;
23060         
23061         var fid = editorcore.frameId;
23062         var etb = this;
23063         function btn(id, toggle, handler){
23064             var xid = fid + '-'+ id ;
23065             return {
23066                 id : xid,
23067                 cmd : id,
23068                 cls : 'x-btn-icon x-edit-'+id,
23069                 enableToggle:toggle !== false,
23070                 scope: _t, // was editor...
23071                 handler:handler||_t.relayBtnCmd,
23072                 clickEvent:'mousedown',
23073                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23074                 tabIndex:-1
23075             };
23076         }
23077         
23078         
23079         
23080         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
23081         this.tb = tb;
23082          // stop form submits
23083         tb.el.on('click', function(e){
23084             e.preventDefault(); // what does this do?
23085         });
23086
23087         if(!this.disable.font) { // && !Roo.isSafari){
23088             /* why no safari for fonts 
23089             editor.fontSelect = tb.el.createChild({
23090                 tag:'select',
23091                 tabIndex: -1,
23092                 cls:'x-font-select',
23093                 html: this.createFontOptions()
23094             });
23095             
23096             editor.fontSelect.on('change', function(){
23097                 var font = editor.fontSelect.dom.value;
23098                 editor.relayCmd('fontname', font);
23099                 editor.deferFocus();
23100             }, editor);
23101             
23102             tb.add(
23103                 editor.fontSelect.dom,
23104                 '-'
23105             );
23106             */
23107             
23108         };
23109         if(!this.disable.formats){
23110             this.formatCombo = new Roo.form.ComboBox({
23111                 store: new Roo.data.SimpleStore({
23112                     id : 'tag',
23113                     fields: ['tag'],
23114                     data : this.formats // from states.js
23115                 }),
23116                 blockFocus : true,
23117                 name : '',
23118                 //autoCreate : {tag: "div",  size: "20"},
23119                 displayField:'tag',
23120                 typeAhead: false,
23121                 mode: 'local',
23122                 editable : false,
23123                 triggerAction: 'all',
23124                 emptyText:'Add tag',
23125                 selectOnFocus:true,
23126                 width:135,
23127                 listeners : {
23128                     'select': function(c, r, i) {
23129                         editorcore.insertTag(r.get('tag'));
23130                         editor.focus();
23131                     }
23132                 }
23133
23134             });
23135             tb.addField(this.formatCombo);
23136             
23137         }
23138         
23139         if(!this.disable.format){
23140             tb.add(
23141                 btn('bold'),
23142                 btn('italic'),
23143                 btn('underline'),
23144                 btn('strikethrough')
23145             );
23146         };
23147         if(!this.disable.fontSize){
23148             tb.add(
23149                 '-',
23150                 
23151                 
23152                 btn('increasefontsize', false, editorcore.adjustFont),
23153                 btn('decreasefontsize', false, editorcore.adjustFont)
23154             );
23155         };
23156         
23157         
23158         if(!this.disable.colors){
23159             tb.add(
23160                 '-', {
23161                     id:editorcore.frameId +'-forecolor',
23162                     cls:'x-btn-icon x-edit-forecolor',
23163                     clickEvent:'mousedown',
23164                     tooltip: this.buttonTips['forecolor'] || undefined,
23165                     tabIndex:-1,
23166                     menu : new Roo.menu.ColorMenu({
23167                         allowReselect: true,
23168                         focus: Roo.emptyFn,
23169                         value:'000000',
23170                         plain:true,
23171                         selectHandler: function(cp, color){
23172                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
23173                             editor.deferFocus();
23174                         },
23175                         scope: editorcore,
23176                         clickEvent:'mousedown'
23177                     })
23178                 }, {
23179                     id:editorcore.frameId +'backcolor',
23180                     cls:'x-btn-icon x-edit-backcolor',
23181                     clickEvent:'mousedown',
23182                     tooltip: this.buttonTips['backcolor'] || undefined,
23183                     tabIndex:-1,
23184                     menu : new Roo.menu.ColorMenu({
23185                         focus: Roo.emptyFn,
23186                         value:'FFFFFF',
23187                         plain:true,
23188                         allowReselect: true,
23189                         selectHandler: function(cp, color){
23190                             if(Roo.isGecko){
23191                                 editorcore.execCmd('useCSS', false);
23192                                 editorcore.execCmd('hilitecolor', color);
23193                                 editorcore.execCmd('useCSS', true);
23194                                 editor.deferFocus();
23195                             }else{
23196                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
23197                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
23198                                 editor.deferFocus();
23199                             }
23200                         },
23201                         scope:editorcore,
23202                         clickEvent:'mousedown'
23203                     })
23204                 }
23205             );
23206         };
23207         // now add all the items...
23208         
23209
23210         if(!this.disable.alignments){
23211             tb.add(
23212                 '-',
23213                 btn('justifyleft'),
23214                 btn('justifycenter'),
23215                 btn('justifyright')
23216             );
23217         };
23218
23219         //if(!Roo.isSafari){
23220             if(!this.disable.links){
23221                 tb.add(
23222                     '-',
23223                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
23224                 );
23225             };
23226
23227             if(!this.disable.lists){
23228                 tb.add(
23229                     '-',
23230                     btn('insertorderedlist'),
23231                     btn('insertunorderedlist')
23232                 );
23233             }
23234             if(!this.disable.sourceEdit){
23235                 tb.add(
23236                     '-',
23237                     btn('sourceedit', true, function(btn){
23238                         this.toggleSourceEdit(btn.pressed);
23239                     })
23240                 );
23241             }
23242         //}
23243         
23244         var smenu = { };
23245         // special menu.. - needs to be tidied up..
23246         if (!this.disable.special) {
23247             smenu = {
23248                 text: "&#169;",
23249                 cls: 'x-edit-none',
23250                 
23251                 menu : {
23252                     items : []
23253                 }
23254             };
23255             for (var i =0; i < this.specialChars.length; i++) {
23256                 smenu.menu.items.push({
23257                     
23258                     html: this.specialChars[i],
23259                     handler: function(a,b) {
23260                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23261                         //editor.insertAtCursor(a.html);
23262                         
23263                     },
23264                     tabIndex:-1
23265                 });
23266             }
23267             
23268             
23269             tb.add(smenu);
23270             
23271             
23272         }
23273         
23274         var cmenu = { };
23275         if (!this.disable.cleanStyles) {
23276             cmenu = {
23277                 cls: 'x-btn-icon x-btn-clear',
23278                 
23279                 menu : {
23280                     items : []
23281                 }
23282             };
23283             for (var i =0; i < this.cleanStyles.length; i++) {
23284                 cmenu.menu.items.push({
23285                     actiontype : this.cleanStyles[i],
23286                     html: 'Remove ' + this.cleanStyles[i],
23287                     handler: function(a,b) {
23288 //                        Roo.log(a);
23289 //                        Roo.log(b);
23290                         var c = Roo.get(editorcore.doc.body);
23291                         c.select('[style]').each(function(s) {
23292                             s.dom.style.removeProperty(a.actiontype);
23293                         });
23294                         editorcore.syncValue();
23295                     },
23296                     tabIndex:-1
23297                 });
23298             }
23299              cmenu.menu.items.push({
23300                 actiontype : 'tablewidths',
23301                 html: 'Remove Table Widths',
23302                 handler: function(a,b) {
23303                     editorcore.cleanTableWidths();
23304                     editorcore.syncValue();
23305                 },
23306                 tabIndex:-1
23307             });
23308             cmenu.menu.items.push({
23309                 actiontype : 'word',
23310                 html: 'Remove MS Word Formating',
23311                 handler: function(a,b) {
23312                     editorcore.cleanWord();
23313                     editorcore.syncValue();
23314                 },
23315                 tabIndex:-1
23316             });
23317             
23318             cmenu.menu.items.push({
23319                 actiontype : 'all',
23320                 html: 'Remove All Styles',
23321                 handler: function(a,b) {
23322                     
23323                     var c = Roo.get(editorcore.doc.body);
23324                     c.select('[style]').each(function(s) {
23325                         s.dom.removeAttribute('style');
23326                     });
23327                     editorcore.syncValue();
23328                 },
23329                 tabIndex:-1
23330             });
23331             
23332             cmenu.menu.items.push({
23333                 actiontype : 'all',
23334                 html: 'Remove All CSS Classes',
23335                 handler: function(a,b) {
23336                     
23337                     var c = Roo.get(editorcore.doc.body);
23338                     c.select('[class]').each(function(s) {
23339                         s.dom.removeAttribute('class');
23340                     });
23341                     editorcore.cleanWord();
23342                     editorcore.syncValue();
23343                 },
23344                 tabIndex:-1
23345             });
23346             
23347              cmenu.menu.items.push({
23348                 actiontype : 'tidy',
23349                 html: 'Tidy HTML Source',
23350                 handler: function(a,b) {
23351                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
23352                     editorcore.syncValue();
23353                 },
23354                 tabIndex:-1
23355             });
23356             
23357             
23358             tb.add(cmenu);
23359         }
23360          
23361         if (!this.disable.specialElements) {
23362             var semenu = {
23363                 text: "Other;",
23364                 cls: 'x-edit-none',
23365                 menu : {
23366                     items : []
23367                 }
23368             };
23369             for (var i =0; i < this.specialElements.length; i++) {
23370                 semenu.menu.items.push(
23371                     Roo.apply({ 
23372                         handler: function(a,b) {
23373                             editor.insertAtCursor(this.ihtml);
23374                         }
23375                     }, this.specialElements[i])
23376                 );
23377                     
23378             }
23379             
23380             tb.add(semenu);
23381             
23382             
23383         }
23384          
23385         
23386         if (this.btns) {
23387             for(var i =0; i< this.btns.length;i++) {
23388                 var b = Roo.factory(this.btns[i],Roo.form);
23389                 b.cls =  'x-edit-none';
23390                 
23391                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23392                     b.cls += ' x-init-enable';
23393                 }
23394                 
23395                 b.scope = editorcore;
23396                 tb.add(b);
23397             }
23398         
23399         }
23400         
23401         
23402         
23403         // disable everything...
23404         
23405         this.tb.items.each(function(item){
23406             
23407            if(
23408                 item.id != editorcore.frameId+ '-sourceedit' && 
23409                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23410             ){
23411                 
23412                 item.disable();
23413             }
23414         });
23415         this.rendered = true;
23416         
23417         // the all the btns;
23418         editor.on('editorevent', this.updateToolbar, this);
23419         // other toolbars need to implement this..
23420         //editor.on('editmodechange', this.updateToolbar, this);
23421     },
23422     
23423     
23424     relayBtnCmd : function(btn) {
23425         this.editorcore.relayCmd(btn.cmd);
23426     },
23427     // private used internally
23428     createLink : function(){
23429         Roo.log("create link?");
23430         var url = prompt(this.createLinkText, this.defaultLinkValue);
23431         if(url && url != 'http:/'+'/'){
23432             this.editorcore.relayCmd('createlink', url);
23433         }
23434     },
23435
23436     
23437     /**
23438      * Protected method that will not generally be called directly. It triggers
23439      * a toolbar update by reading the markup state of the current selection in the editor.
23440      */
23441     updateToolbar: function(){
23442
23443         if(!this.editorcore.activated){
23444             this.editor.onFirstFocus();
23445             return;
23446         }
23447
23448         var btns = this.tb.items.map, 
23449             doc = this.editorcore.doc,
23450             frameId = this.editorcore.frameId;
23451
23452         if(!this.disable.font && !Roo.isSafari){
23453             /*
23454             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23455             if(name != this.fontSelect.dom.value){
23456                 this.fontSelect.dom.value = name;
23457             }
23458             */
23459         }
23460         if(!this.disable.format){
23461             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23462             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23463             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23464             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23465         }
23466         if(!this.disable.alignments){
23467             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23468             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23469             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23470         }
23471         if(!Roo.isSafari && !this.disable.lists){
23472             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23473             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23474         }
23475         
23476         var ans = this.editorcore.getAllAncestors();
23477         if (this.formatCombo) {
23478             
23479             
23480             var store = this.formatCombo.store;
23481             this.formatCombo.setValue("");
23482             for (var i =0; i < ans.length;i++) {
23483                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23484                     // select it..
23485                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23486                     break;
23487                 }
23488             }
23489         }
23490         
23491         
23492         
23493         // hides menus... - so this cant be on a menu...
23494         Roo.menu.MenuMgr.hideAll();
23495
23496         //this.editorsyncValue();
23497     },
23498    
23499     
23500     createFontOptions : function(){
23501         var buf = [], fs = this.fontFamilies, ff, lc;
23502         
23503         
23504         
23505         for(var i = 0, len = fs.length; i< len; i++){
23506             ff = fs[i];
23507             lc = ff.toLowerCase();
23508             buf.push(
23509                 '<option value="',lc,'" style="font-family:',ff,';"',
23510                     (this.defaultFont == lc ? ' selected="true">' : '>'),
23511                     ff,
23512                 '</option>'
23513             );
23514         }
23515         return buf.join('');
23516     },
23517     
23518     toggleSourceEdit : function(sourceEditMode){
23519         
23520         Roo.log("toolbar toogle");
23521         if(sourceEditMode === undefined){
23522             sourceEditMode = !this.sourceEditMode;
23523         }
23524         this.sourceEditMode = sourceEditMode === true;
23525         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23526         // just toggle the button?
23527         if(btn.pressed !== this.sourceEditMode){
23528             btn.toggle(this.sourceEditMode);
23529             return;
23530         }
23531         
23532         if(sourceEditMode){
23533             Roo.log("disabling buttons");
23534             this.tb.items.each(function(item){
23535                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23536                     item.disable();
23537                 }
23538             });
23539           
23540         }else{
23541             Roo.log("enabling buttons");
23542             if(this.editorcore.initialized){
23543                 this.tb.items.each(function(item){
23544                     item.enable();
23545                 });
23546             }
23547             
23548         }
23549         Roo.log("calling toggole on editor");
23550         // tell the editor that it's been pressed..
23551         this.editor.toggleSourceEdit(sourceEditMode);
23552        
23553     },
23554      /**
23555      * Object collection of toolbar tooltips for the buttons in the editor. The key
23556      * is the command id associated with that button and the value is a valid QuickTips object.
23557      * For example:
23558 <pre><code>
23559 {
23560     bold : {
23561         title: 'Bold (Ctrl+B)',
23562         text: 'Make the selected text bold.',
23563         cls: 'x-html-editor-tip'
23564     },
23565     italic : {
23566         title: 'Italic (Ctrl+I)',
23567         text: 'Make the selected text italic.',
23568         cls: 'x-html-editor-tip'
23569     },
23570     ...
23571 </code></pre>
23572     * @type Object
23573      */
23574     buttonTips : {
23575         bold : {
23576             title: 'Bold (Ctrl+B)',
23577             text: 'Make the selected text bold.',
23578             cls: 'x-html-editor-tip'
23579         },
23580         italic : {
23581             title: 'Italic (Ctrl+I)',
23582             text: 'Make the selected text italic.',
23583             cls: 'x-html-editor-tip'
23584         },
23585         underline : {
23586             title: 'Underline (Ctrl+U)',
23587             text: 'Underline the selected text.',
23588             cls: 'x-html-editor-tip'
23589         },
23590         strikethrough : {
23591             title: 'Strikethrough',
23592             text: 'Strikethrough the selected text.',
23593             cls: 'x-html-editor-tip'
23594         },
23595         increasefontsize : {
23596             title: 'Grow Text',
23597             text: 'Increase the font size.',
23598             cls: 'x-html-editor-tip'
23599         },
23600         decreasefontsize : {
23601             title: 'Shrink Text',
23602             text: 'Decrease the font size.',
23603             cls: 'x-html-editor-tip'
23604         },
23605         backcolor : {
23606             title: 'Text Highlight Color',
23607             text: 'Change the background color of the selected text.',
23608             cls: 'x-html-editor-tip'
23609         },
23610         forecolor : {
23611             title: 'Font Color',
23612             text: 'Change the color of the selected text.',
23613             cls: 'x-html-editor-tip'
23614         },
23615         justifyleft : {
23616             title: 'Align Text Left',
23617             text: 'Align text to the left.',
23618             cls: 'x-html-editor-tip'
23619         },
23620         justifycenter : {
23621             title: 'Center Text',
23622             text: 'Center text in the editor.',
23623             cls: 'x-html-editor-tip'
23624         },
23625         justifyright : {
23626             title: 'Align Text Right',
23627             text: 'Align text to the right.',
23628             cls: 'x-html-editor-tip'
23629         },
23630         insertunorderedlist : {
23631             title: 'Bullet List',
23632             text: 'Start a bulleted list.',
23633             cls: 'x-html-editor-tip'
23634         },
23635         insertorderedlist : {
23636             title: 'Numbered List',
23637             text: 'Start a numbered list.',
23638             cls: 'x-html-editor-tip'
23639         },
23640         createlink : {
23641             title: 'Hyperlink',
23642             text: 'Make the selected text a hyperlink.',
23643             cls: 'x-html-editor-tip'
23644         },
23645         sourceedit : {
23646             title: 'Source Edit',
23647             text: 'Switch to source editing mode.',
23648             cls: 'x-html-editor-tip'
23649         }
23650     },
23651     // private
23652     onDestroy : function(){
23653         if(this.rendered){
23654             
23655             this.tb.items.each(function(item){
23656                 if(item.menu){
23657                     item.menu.removeAll();
23658                     if(item.menu.el){
23659                         item.menu.el.destroy();
23660                     }
23661                 }
23662                 item.destroy();
23663             });
23664              
23665         }
23666     },
23667     onFirstFocus: function() {
23668         this.tb.items.each(function(item){
23669            item.enable();
23670         });
23671     }
23672 });
23673
23674
23675
23676
23677 // <script type="text/javascript">
23678 /*
23679  * Based on
23680  * Ext JS Library 1.1.1
23681  * Copyright(c) 2006-2007, Ext JS, LLC.
23682  *  
23683  
23684  */
23685
23686  
23687 /**
23688  * @class Roo.form.HtmlEditor.ToolbarContext
23689  * Context Toolbar
23690  * 
23691  * Usage:
23692  *
23693  new Roo.form.HtmlEditor({
23694     ....
23695     toolbars : [
23696         { xtype: 'ToolbarStandard', styles : {} }
23697         { xtype: 'ToolbarContext', disable : {} }
23698     ]
23699 })
23700
23701      
23702  * 
23703  * @config : {Object} disable List of elements to disable.. (not done yet.)
23704  * @config : {Object} styles  Map of styles available.
23705  * 
23706  */
23707
23708 Roo.form.HtmlEditor.ToolbarContext = function(config)
23709 {
23710     
23711     Roo.apply(this, config);
23712     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23713     // dont call parent... till later.
23714     this.styles = this.styles || {};
23715 }
23716
23717  
23718
23719 Roo.form.HtmlEditor.ToolbarContext.types = {
23720     'IMG' : {
23721         width : {
23722             title: "Width",
23723             width: 40
23724         },
23725         height:  {
23726             title: "Height",
23727             width: 40
23728         },
23729         align: {
23730             title: "Align",
23731             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
23732             width : 80
23733             
23734         },
23735         border: {
23736             title: "Border",
23737             width: 40
23738         },
23739         alt: {
23740             title: "Alt",
23741             width: 120
23742         },
23743         src : {
23744             title: "Src",
23745             width: 220
23746         }
23747         
23748     },
23749     'A' : {
23750         name : {
23751             title: "Name",
23752             width: 50
23753         },
23754         target:  {
23755             title: "Target",
23756             width: 120
23757         },
23758         href:  {
23759             title: "Href",
23760             width: 220
23761         } // border?
23762         
23763     },
23764     'TABLE' : {
23765         rows : {
23766             title: "Rows",
23767             width: 20
23768         },
23769         cols : {
23770             title: "Cols",
23771             width: 20
23772         },
23773         width : {
23774             title: "Width",
23775             width: 40
23776         },
23777         height : {
23778             title: "Height",
23779             width: 40
23780         },
23781         border : {
23782             title: "Border",
23783             width: 20
23784         }
23785     },
23786     'TD' : {
23787         width : {
23788             title: "Width",
23789             width: 40
23790         },
23791         height : {
23792             title: "Height",
23793             width: 40
23794         },   
23795         align: {
23796             title: "Align",
23797             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
23798             width: 80
23799         },
23800         valign: {
23801             title: "Valign",
23802             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
23803             width: 80
23804         },
23805         colspan: {
23806             title: "Colspan",
23807             width: 20
23808             
23809         },
23810          'font-family'  : {
23811             title : "Font",
23812             style : 'fontFamily',
23813             displayField: 'display',
23814             optname : 'font-family',
23815             width: 140
23816         }
23817     },
23818     'INPUT' : {
23819         name : {
23820             title: "name",
23821             width: 120
23822         },
23823         value : {
23824             title: "Value",
23825             width: 120
23826         },
23827         width : {
23828             title: "Width",
23829             width: 40
23830         }
23831     },
23832     'LABEL' : {
23833         'for' : {
23834             title: "For",
23835             width: 120
23836         }
23837     },
23838     'TEXTAREA' : {
23839           name : {
23840             title: "name",
23841             width: 120
23842         },
23843         rows : {
23844             title: "Rows",
23845             width: 20
23846         },
23847         cols : {
23848             title: "Cols",
23849             width: 20
23850         }
23851     },
23852     'SELECT' : {
23853         name : {
23854             title: "name",
23855             width: 120
23856         },
23857         selectoptions : {
23858             title: "Options",
23859             width: 200
23860         }
23861     },
23862     
23863     // should we really allow this??
23864     // should this just be 
23865     'BODY' : {
23866         title : {
23867             title: "Title",
23868             width: 200,
23869             disabled : true
23870         }
23871     },
23872     'SPAN' : {
23873         'font-family'  : {
23874             title : "Font",
23875             style : 'fontFamily',
23876             displayField: 'display',
23877             optname : 'font-family',
23878             width: 140
23879         }
23880     },
23881     'DIV' : {
23882         'font-family'  : {
23883             title : "Font",
23884             style : 'fontFamily',
23885             displayField: 'display',
23886             optname : 'font-family',
23887             width: 140
23888         }
23889     },
23890      'P' : {
23891         'font-family'  : {
23892             title : "Font",
23893             style : 'fontFamily',
23894             displayField: 'display',
23895             optname : 'font-family',
23896             width: 140
23897         }
23898     },
23899     
23900     '*' : {
23901         // empty..
23902     }
23903
23904 };
23905
23906 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
23907 Roo.form.HtmlEditor.ToolbarContext.stores = false;
23908
23909 Roo.form.HtmlEditor.ToolbarContext.options = {
23910         'font-family'  : [ 
23911                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
23912                 [ 'Courier New', 'Courier New'],
23913                 [ 'Tahoma', 'Tahoma'],
23914                 [ 'Times New Roman,serif', 'Times'],
23915                 [ 'Verdana','Verdana' ]
23916         ]
23917 };
23918
23919 // fixme - these need to be configurable..
23920  
23921
23922 //Roo.form.HtmlEditor.ToolbarContext.types
23923
23924
23925 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
23926     
23927     tb: false,
23928     
23929     rendered: false,
23930     
23931     editor : false,
23932     editorcore : false,
23933     /**
23934      * @cfg {Object} disable  List of toolbar elements to disable
23935          
23936      */
23937     disable : false,
23938     /**
23939      * @cfg {Object} styles List of styles 
23940      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
23941      *
23942      * These must be defined in the page, so they get rendered correctly..
23943      * .headline { }
23944      * TD.underline { }
23945      * 
23946      */
23947     styles : false,
23948     
23949     options: false,
23950     
23951     toolbars : false,
23952     
23953     init : function(editor)
23954     {
23955         this.editor = editor;
23956         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23957         var editorcore = this.editorcore;
23958         
23959         var fid = editorcore.frameId;
23960         var etb = this;
23961         function btn(id, toggle, handler){
23962             var xid = fid + '-'+ id ;
23963             return {
23964                 id : xid,
23965                 cmd : id,
23966                 cls : 'x-btn-icon x-edit-'+id,
23967                 enableToggle:toggle !== false,
23968                 scope: editorcore, // was editor...
23969                 handler:handler||editorcore.relayBtnCmd,
23970                 clickEvent:'mousedown',
23971                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23972                 tabIndex:-1
23973             };
23974         }
23975         // create a new element.
23976         var wdiv = editor.wrap.createChild({
23977                 tag: 'div'
23978             }, editor.wrap.dom.firstChild.nextSibling, true);
23979         
23980         // can we do this more than once??
23981         
23982          // stop form submits
23983       
23984  
23985         // disable everything...
23986         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23987         this.toolbars = {};
23988            
23989         for (var i in  ty) {
23990           
23991             this.toolbars[i] = this.buildToolbar(ty[i],i);
23992         }
23993         this.tb = this.toolbars.BODY;
23994         this.tb.el.show();
23995         this.buildFooter();
23996         this.footer.show();
23997         editor.on('hide', function( ) { this.footer.hide() }, this);
23998         editor.on('show', function( ) { this.footer.show() }, this);
23999         
24000          
24001         this.rendered = true;
24002         
24003         // the all the btns;
24004         editor.on('editorevent', this.updateToolbar, this);
24005         // other toolbars need to implement this..
24006         //editor.on('editmodechange', this.updateToolbar, this);
24007     },
24008     
24009     
24010     
24011     /**
24012      * Protected method that will not generally be called directly. It triggers
24013      * a toolbar update by reading the markup state of the current selection in the editor.
24014      *
24015      * Note you can force an update by calling on('editorevent', scope, false)
24016      */
24017     updateToolbar: function(editor,ev,sel){
24018
24019         //Roo.log(ev);
24020         // capture mouse up - this is handy for selecting images..
24021         // perhaps should go somewhere else...
24022         if(!this.editorcore.activated){
24023              this.editor.onFirstFocus();
24024             return;
24025         }
24026         
24027         
24028         
24029         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
24030         // selectNode - might want to handle IE?
24031         if (ev &&
24032             (ev.type == 'mouseup' || ev.type == 'click' ) &&
24033             ev.target && ev.target.tagName == 'IMG') {
24034             // they have click on an image...
24035             // let's see if we can change the selection...
24036             sel = ev.target;
24037          
24038               var nodeRange = sel.ownerDocument.createRange();
24039             try {
24040                 nodeRange.selectNode(sel);
24041             } catch (e) {
24042                 nodeRange.selectNodeContents(sel);
24043             }
24044             //nodeRange.collapse(true);
24045             var s = this.editorcore.win.getSelection();
24046             s.removeAllRanges();
24047             s.addRange(nodeRange);
24048         }  
24049         
24050       
24051         var updateFooter = sel ? false : true;
24052         
24053         
24054         var ans = this.editorcore.getAllAncestors();
24055         
24056         // pick
24057         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24058         
24059         if (!sel) { 
24060             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
24061             sel = sel ? sel : this.editorcore.doc.body;
24062             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
24063             
24064         }
24065         // pick a menu that exists..
24066         var tn = sel.tagName.toUpperCase();
24067         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
24068         
24069         tn = sel.tagName.toUpperCase();
24070         
24071         var lastSel = this.tb.selectedNode;
24072         
24073         this.tb.selectedNode = sel;
24074         
24075         // if current menu does not match..
24076         
24077         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
24078                 
24079             this.tb.el.hide();
24080             ///console.log("show: " + tn);
24081             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
24082             this.tb.el.show();
24083             // update name
24084             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
24085             
24086             
24087             // update attributes
24088             if (this.tb.fields) {
24089                 this.tb.fields.each(function(e) {
24090                     if (e.stylename) {
24091                         e.setValue(sel.style[e.stylename]);
24092                         return;
24093                     } 
24094                    e.setValue(sel.getAttribute(e.attrname));
24095                 });
24096             }
24097             
24098             var hasStyles = false;
24099             for(var i in this.styles) {
24100                 hasStyles = true;
24101                 break;
24102             }
24103             
24104             // update styles
24105             if (hasStyles) { 
24106                 var st = this.tb.fields.item(0);
24107                 
24108                 st.store.removeAll();
24109                
24110                 
24111                 var cn = sel.className.split(/\s+/);
24112                 
24113                 var avs = [];
24114                 if (this.styles['*']) {
24115                     
24116                     Roo.each(this.styles['*'], function(v) {
24117                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24118                     });
24119                 }
24120                 if (this.styles[tn]) { 
24121                     Roo.each(this.styles[tn], function(v) {
24122                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24123                     });
24124                 }
24125                 
24126                 st.store.loadData(avs);
24127                 st.collapse();
24128                 st.setValue(cn);
24129             }
24130             // flag our selected Node.
24131             this.tb.selectedNode = sel;
24132            
24133            
24134             Roo.menu.MenuMgr.hideAll();
24135
24136         }
24137         
24138         if (!updateFooter) {
24139             //this.footDisp.dom.innerHTML = ''; 
24140             return;
24141         }
24142         // update the footer
24143         //
24144         var html = '';
24145         
24146         this.footerEls = ans.reverse();
24147         Roo.each(this.footerEls, function(a,i) {
24148             if (!a) { return; }
24149             html += html.length ? ' &gt; '  :  '';
24150             
24151             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
24152             
24153         });
24154        
24155         // 
24156         var sz = this.footDisp.up('td').getSize();
24157         this.footDisp.dom.style.width = (sz.width -10) + 'px';
24158         this.footDisp.dom.style.marginLeft = '5px';
24159         
24160         this.footDisp.dom.style.overflow = 'hidden';
24161         
24162         this.footDisp.dom.innerHTML = html;
24163             
24164         //this.editorsyncValue();
24165     },
24166      
24167     
24168    
24169        
24170     // private
24171     onDestroy : function(){
24172         if(this.rendered){
24173             
24174             this.tb.items.each(function(item){
24175                 if(item.menu){
24176                     item.menu.removeAll();
24177                     if(item.menu.el){
24178                         item.menu.el.destroy();
24179                     }
24180                 }
24181                 item.destroy();
24182             });
24183              
24184         }
24185     },
24186     onFirstFocus: function() {
24187         // need to do this for all the toolbars..
24188         this.tb.items.each(function(item){
24189            item.enable();
24190         });
24191     },
24192     buildToolbar: function(tlist, nm)
24193     {
24194         var editor = this.editor;
24195         var editorcore = this.editorcore;
24196          // create a new element.
24197         var wdiv = editor.wrap.createChild({
24198                 tag: 'div'
24199             }, editor.wrap.dom.firstChild.nextSibling, true);
24200         
24201        
24202         var tb = new Roo.Toolbar(wdiv);
24203         // add the name..
24204         
24205         tb.add(nm+ ":&nbsp;");
24206         
24207         var styles = [];
24208         for(var i in this.styles) {
24209             styles.push(i);
24210         }
24211         
24212         // styles...
24213         if (styles && styles.length) {
24214             
24215             // this needs a multi-select checkbox...
24216             tb.addField( new Roo.form.ComboBox({
24217                 store: new Roo.data.SimpleStore({
24218                     id : 'val',
24219                     fields: ['val', 'selected'],
24220                     data : [] 
24221                 }),
24222                 name : '-roo-edit-className',
24223                 attrname : 'className',
24224                 displayField: 'val',
24225                 typeAhead: false,
24226                 mode: 'local',
24227                 editable : false,
24228                 triggerAction: 'all',
24229                 emptyText:'Select Style',
24230                 selectOnFocus:true,
24231                 width: 130,
24232                 listeners : {
24233                     'select': function(c, r, i) {
24234                         // initial support only for on class per el..
24235                         tb.selectedNode.className =  r ? r.get('val') : '';
24236                         editorcore.syncValue();
24237                     }
24238                 }
24239     
24240             }));
24241         }
24242         
24243         var tbc = Roo.form.HtmlEditor.ToolbarContext;
24244         var tbops = tbc.options;
24245         
24246         for (var i in tlist) {
24247             
24248             var item = tlist[i];
24249             tb.add(item.title + ":&nbsp;");
24250             
24251             
24252             //optname == used so you can configure the options available..
24253             var opts = item.opts ? item.opts : false;
24254             if (item.optname) {
24255                 opts = tbops[item.optname];
24256            
24257             }
24258             
24259             if (opts) {
24260                 // opts == pulldown..
24261                 tb.addField( new Roo.form.ComboBox({
24262                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24263                         id : 'val',
24264                         fields: ['val', 'display'],
24265                         data : opts  
24266                     }),
24267                     name : '-roo-edit-' + i,
24268                     attrname : i,
24269                     stylename : item.style ? item.style : false,
24270                     displayField: item.displayField ? item.displayField : 'val',
24271                     valueField :  'val',
24272                     typeAhead: false,
24273                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
24274                     editable : false,
24275                     triggerAction: 'all',
24276                     emptyText:'Select',
24277                     selectOnFocus:true,
24278                     width: item.width ? item.width  : 130,
24279                     listeners : {
24280                         'select': function(c, r, i) {
24281                             if (c.stylename) {
24282                                 tb.selectedNode.style[c.stylename] =  r.get('val');
24283                                 return;
24284                             }
24285                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24286                         }
24287                     }
24288
24289                 }));
24290                 continue;
24291                     
24292                  
24293                 
24294                 tb.addField( new Roo.form.TextField({
24295                     name: i,
24296                     width: 100,
24297                     //allowBlank:false,
24298                     value: ''
24299                 }));
24300                 continue;
24301             }
24302             tb.addField( new Roo.form.TextField({
24303                 name: '-roo-edit-' + i,
24304                 attrname : i,
24305                 
24306                 width: item.width,
24307                 //allowBlank:true,
24308                 value: '',
24309                 listeners: {
24310                     'change' : function(f, nv, ov) {
24311                         tb.selectedNode.setAttribute(f.attrname, nv);
24312                         editorcore.syncValue();
24313                     }
24314                 }
24315             }));
24316              
24317         }
24318         
24319         var _this = this;
24320         
24321         if(nm == 'BODY'){
24322             tb.addSeparator();
24323         
24324             tb.addButton( {
24325                 text: 'Stylesheets',
24326
24327                 listeners : {
24328                     click : function ()
24329                     {
24330                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
24331                     }
24332                 }
24333             });
24334         }
24335         
24336         tb.addFill();
24337         tb.addButton( {
24338             text: 'Remove Tag',
24339     
24340             listeners : {
24341                 click : function ()
24342                 {
24343                     // remove
24344                     // undo does not work.
24345                      
24346                     var sn = tb.selectedNode;
24347                     
24348                     var pn = sn.parentNode;
24349                     
24350                     var stn =  sn.childNodes[0];
24351                     var en = sn.childNodes[sn.childNodes.length - 1 ];
24352                     while (sn.childNodes.length) {
24353                         var node = sn.childNodes[0];
24354                         sn.removeChild(node);
24355                         //Roo.log(node);
24356                         pn.insertBefore(node, sn);
24357                         
24358                     }
24359                     pn.removeChild(sn);
24360                     var range = editorcore.createRange();
24361         
24362                     range.setStart(stn,0);
24363                     range.setEnd(en,0); //????
24364                     //range.selectNode(sel);
24365                     
24366                     
24367                     var selection = editorcore.getSelection();
24368                     selection.removeAllRanges();
24369                     selection.addRange(range);
24370                     
24371                     
24372                     
24373                     //_this.updateToolbar(null, null, pn);
24374                     _this.updateToolbar(null, null, null);
24375                     _this.footDisp.dom.innerHTML = ''; 
24376                 }
24377             }
24378             
24379                     
24380                 
24381             
24382         });
24383         
24384         
24385         tb.el.on('click', function(e){
24386             e.preventDefault(); // what does this do?
24387         });
24388         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24389         tb.el.hide();
24390         tb.name = nm;
24391         // dont need to disable them... as they will get hidden
24392         return tb;
24393          
24394         
24395     },
24396     buildFooter : function()
24397     {
24398         
24399         var fel = this.editor.wrap.createChild();
24400         this.footer = new Roo.Toolbar(fel);
24401         // toolbar has scrolly on left / right?
24402         var footDisp= new Roo.Toolbar.Fill();
24403         var _t = this;
24404         this.footer.add(
24405             {
24406                 text : '&lt;',
24407                 xtype: 'Button',
24408                 handler : function() {
24409                     _t.footDisp.scrollTo('left',0,true)
24410                 }
24411             }
24412         );
24413         this.footer.add( footDisp );
24414         this.footer.add( 
24415             {
24416                 text : '&gt;',
24417                 xtype: 'Button',
24418                 handler : function() {
24419                     // no animation..
24420                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24421                 }
24422             }
24423         );
24424         var fel = Roo.get(footDisp.el);
24425         fel.addClass('x-editor-context');
24426         this.footDispWrap = fel; 
24427         this.footDispWrap.overflow  = 'hidden';
24428         
24429         this.footDisp = fel.createChild();
24430         this.footDispWrap.on('click', this.onContextClick, this)
24431         
24432         
24433     },
24434     onContextClick : function (ev,dom)
24435     {
24436         ev.preventDefault();
24437         var  cn = dom.className;
24438         //Roo.log(cn);
24439         if (!cn.match(/x-ed-loc-/)) {
24440             return;
24441         }
24442         var n = cn.split('-').pop();
24443         var ans = this.footerEls;
24444         var sel = ans[n];
24445         
24446          // pick
24447         var range = this.editorcore.createRange();
24448         
24449         range.selectNodeContents(sel);
24450         //range.selectNode(sel);
24451         
24452         
24453         var selection = this.editorcore.getSelection();
24454         selection.removeAllRanges();
24455         selection.addRange(range);
24456         
24457         
24458         
24459         this.updateToolbar(null, null, sel);
24460         
24461         
24462     }
24463     
24464     
24465     
24466     
24467     
24468 });
24469
24470
24471
24472
24473
24474 /*
24475  * Based on:
24476  * Ext JS Library 1.1.1
24477  * Copyright(c) 2006-2007, Ext JS, LLC.
24478  *
24479  * Originally Released Under LGPL - original licence link has changed is not relivant.
24480  *
24481  * Fork - LGPL
24482  * <script type="text/javascript">
24483  */
24484  
24485 /**
24486  * @class Roo.form.BasicForm
24487  * @extends Roo.util.Observable
24488  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24489  * @constructor
24490  * @param {String/HTMLElement/Roo.Element} el The form element or its id
24491  * @param {Object} config Configuration options
24492  */
24493 Roo.form.BasicForm = function(el, config){
24494     this.allItems = [];
24495     this.childForms = [];
24496     Roo.apply(this, config);
24497     /*
24498      * The Roo.form.Field items in this form.
24499      * @type MixedCollection
24500      */
24501      
24502      
24503     this.items = new Roo.util.MixedCollection(false, function(o){
24504         return o.id || (o.id = Roo.id());
24505     });
24506     this.addEvents({
24507         /**
24508          * @event beforeaction
24509          * Fires before any action is performed. Return false to cancel the action.
24510          * @param {Form} this
24511          * @param {Action} action The action to be performed
24512          */
24513         beforeaction: true,
24514         /**
24515          * @event actionfailed
24516          * Fires when an action fails.
24517          * @param {Form} this
24518          * @param {Action} action The action that failed
24519          */
24520         actionfailed : true,
24521         /**
24522          * @event actioncomplete
24523          * Fires when an action is completed.
24524          * @param {Form} this
24525          * @param {Action} action The action that completed
24526          */
24527         actioncomplete : true
24528     });
24529     if(el){
24530         this.initEl(el);
24531     }
24532     Roo.form.BasicForm.superclass.constructor.call(this);
24533     
24534     Roo.form.BasicForm.popover.apply();
24535 };
24536
24537 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24538     /**
24539      * @cfg {String} method
24540      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24541      */
24542     /**
24543      * @cfg {DataReader} reader
24544      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24545      * This is optional as there is built-in support for processing JSON.
24546      */
24547     /**
24548      * @cfg {DataReader} errorReader
24549      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24550      * This is completely optional as there is built-in support for processing JSON.
24551      */
24552     /**
24553      * @cfg {String} url
24554      * The URL to use for form actions if one isn't supplied in the action options.
24555      */
24556     /**
24557      * @cfg {Boolean} fileUpload
24558      * Set to true if this form is a file upload.
24559      */
24560      
24561     /**
24562      * @cfg {Object} baseParams
24563      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24564      */
24565      /**
24566      
24567     /**
24568      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24569      */
24570     timeout: 30,
24571
24572     // private
24573     activeAction : null,
24574
24575     /**
24576      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24577      * or setValues() data instead of when the form was first created.
24578      */
24579     trackResetOnLoad : false,
24580     
24581     
24582     /**
24583      * childForms - used for multi-tab forms
24584      * @type {Array}
24585      */
24586     childForms : false,
24587     
24588     /**
24589      * allItems - full list of fields.
24590      * @type {Array}
24591      */
24592     allItems : false,
24593     
24594     /**
24595      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24596      * element by passing it or its id or mask the form itself by passing in true.
24597      * @type Mixed
24598      */
24599     waitMsgTarget : false,
24600     
24601     /**
24602      * @type Boolean
24603      */
24604     disableMask : false,
24605     
24606     /**
24607      * @cfg {Boolean} errorMask (true|false) default false
24608      */
24609     errorMask : false,
24610     
24611     /**
24612      * @cfg {Number} maskOffset Default 100
24613      */
24614     maskOffset : 100,
24615
24616     // private
24617     initEl : function(el){
24618         this.el = Roo.get(el);
24619         this.id = this.el.id || Roo.id();
24620         this.el.on('submit', this.onSubmit, this);
24621         this.el.addClass('x-form');
24622     },
24623
24624     // private
24625     onSubmit : function(e){
24626         e.stopEvent();
24627     },
24628
24629     /**
24630      * Returns true if client-side validation on the form is successful.
24631      * @return Boolean
24632      */
24633     isValid : function(){
24634         var valid = true;
24635         var target = false;
24636         this.items.each(function(f){
24637             if(f.validate()){
24638                 return;
24639             }
24640             
24641             valid = false;
24642                 
24643             if(!target && f.el.isVisible(true)){
24644                 target = f;
24645             }
24646         });
24647         
24648         if(this.errorMask && !valid){
24649             Roo.form.BasicForm.popover.mask(this, target);
24650         }
24651         
24652         return valid;
24653     },
24654     /**
24655      * Returns array of invalid form fields.
24656      * @return Array
24657      */
24658     
24659     invalidFields : function()
24660     {
24661         var ret = [];
24662         this.items.each(function(f){
24663             if(f.validate()){
24664                 return;
24665             }
24666             ret.push(f);
24667             
24668         });
24669         
24670         return ret;
24671     },
24672     
24673     
24674     /**
24675      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
24676      * @return Boolean
24677      */
24678     isDirty : function(){
24679         var dirty = false;
24680         this.items.each(function(f){
24681            if(f.isDirty()){
24682                dirty = true;
24683                return false;
24684            }
24685         });
24686         return dirty;
24687     },
24688     
24689     /**
24690      * Returns true if any fields in this form have changed since their original load. (New version)
24691      * @return Boolean
24692      */
24693     
24694     hasChanged : function()
24695     {
24696         var dirty = false;
24697         this.items.each(function(f){
24698            if(f.hasChanged()){
24699                dirty = true;
24700                return false;
24701            }
24702         });
24703         return dirty;
24704         
24705     },
24706     /**
24707      * Resets all hasChanged to 'false' -
24708      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24709      * So hasChanged storage is only to be used for this purpose
24710      * @return Boolean
24711      */
24712     resetHasChanged : function()
24713     {
24714         this.items.each(function(f){
24715            f.resetHasChanged();
24716         });
24717         
24718     },
24719     
24720     
24721     /**
24722      * Performs a predefined action (submit or load) or custom actions you define on this form.
24723      * @param {String} actionName The name of the action type
24724      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
24725      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24726      * accept other config options):
24727      * <pre>
24728 Property          Type             Description
24729 ----------------  ---------------  ----------------------------------------------------------------------------------
24730 url               String           The url for the action (defaults to the form's url)
24731 method            String           The form method to use (defaults to the form's method, or POST if not defined)
24732 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
24733 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
24734                                    validate the form on the client (defaults to false)
24735      * </pre>
24736      * @return {BasicForm} this
24737      */
24738     doAction : function(action, options){
24739         if(typeof action == 'string'){
24740             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
24741         }
24742         if(this.fireEvent('beforeaction', this, action) !== false){
24743             this.beforeAction(action);
24744             action.run.defer(100, action);
24745         }
24746         return this;
24747     },
24748
24749     /**
24750      * Shortcut to do a submit action.
24751      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24752      * @return {BasicForm} this
24753      */
24754     submit : function(options){
24755         this.doAction('submit', options);
24756         return this;
24757     },
24758
24759     /**
24760      * Shortcut to do a load action.
24761      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24762      * @return {BasicForm} this
24763      */
24764     load : function(options){
24765         this.doAction('load', options);
24766         return this;
24767     },
24768
24769     /**
24770      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
24771      * @param {Record} record The record to edit
24772      * @return {BasicForm} this
24773      */
24774     updateRecord : function(record){
24775         record.beginEdit();
24776         var fs = record.fields;
24777         fs.each(function(f){
24778             var field = this.findField(f.name);
24779             if(field){
24780                 record.set(f.name, field.getValue());
24781             }
24782         }, this);
24783         record.endEdit();
24784         return this;
24785     },
24786
24787     /**
24788      * Loads an Roo.data.Record into this form.
24789      * @param {Record} record The record to load
24790      * @return {BasicForm} this
24791      */
24792     loadRecord : function(record){
24793         this.setValues(record.data);
24794         return this;
24795     },
24796
24797     // private
24798     beforeAction : function(action){
24799         var o = action.options;
24800         
24801         if(!this.disableMask) {
24802             if(this.waitMsgTarget === true){
24803                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
24804             }else if(this.waitMsgTarget){
24805                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
24806                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
24807             }else {
24808                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
24809             }
24810         }
24811         
24812          
24813     },
24814
24815     // private
24816     afterAction : function(action, success){
24817         this.activeAction = null;
24818         var o = action.options;
24819         
24820         if(!this.disableMask) {
24821             if(this.waitMsgTarget === true){
24822                 this.el.unmask();
24823             }else if(this.waitMsgTarget){
24824                 this.waitMsgTarget.unmask();
24825             }else{
24826                 Roo.MessageBox.updateProgress(1);
24827                 Roo.MessageBox.hide();
24828             }
24829         }
24830         
24831         if(success){
24832             if(o.reset){
24833                 this.reset();
24834             }
24835             Roo.callback(o.success, o.scope, [this, action]);
24836             this.fireEvent('actioncomplete', this, action);
24837             
24838         }else{
24839             
24840             // failure condition..
24841             // we have a scenario where updates need confirming.
24842             // eg. if a locking scenario exists..
24843             // we look for { errors : { needs_confirm : true }} in the response.
24844             if (
24845                 (typeof(action.result) != 'undefined')  &&
24846                 (typeof(action.result.errors) != 'undefined')  &&
24847                 (typeof(action.result.errors.needs_confirm) != 'undefined')
24848            ){
24849                 var _t = this;
24850                 Roo.MessageBox.confirm(
24851                     "Change requires confirmation",
24852                     action.result.errorMsg,
24853                     function(r) {
24854                         if (r != 'yes') {
24855                             return;
24856                         }
24857                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
24858                     }
24859                     
24860                 );
24861                 
24862                 
24863                 
24864                 return;
24865             }
24866             
24867             Roo.callback(o.failure, o.scope, [this, action]);
24868             // show an error message if no failed handler is set..
24869             if (!this.hasListener('actionfailed')) {
24870                 Roo.MessageBox.alert("Error",
24871                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
24872                         action.result.errorMsg :
24873                         "Saving Failed, please check your entries or try again"
24874                 );
24875             }
24876             
24877             this.fireEvent('actionfailed', this, action);
24878         }
24879         
24880     },
24881
24882     /**
24883      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
24884      * @param {String} id The value to search for
24885      * @return Field
24886      */
24887     findField : function(id){
24888         var field = this.items.get(id);
24889         if(!field){
24890             this.items.each(function(f){
24891                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
24892                     field = f;
24893                     return false;
24894                 }
24895             });
24896         }
24897         return field || null;
24898     },
24899
24900     /**
24901      * Add a secondary form to this one, 
24902      * Used to provide tabbed forms. One form is primary, with hidden values 
24903      * which mirror the elements from the other forms.
24904      * 
24905      * @param {Roo.form.Form} form to add.
24906      * 
24907      */
24908     addForm : function(form)
24909     {
24910        
24911         if (this.childForms.indexOf(form) > -1) {
24912             // already added..
24913             return;
24914         }
24915         this.childForms.push(form);
24916         var n = '';
24917         Roo.each(form.allItems, function (fe) {
24918             
24919             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
24920             if (this.findField(n)) { // already added..
24921                 return;
24922             }
24923             var add = new Roo.form.Hidden({
24924                 name : n
24925             });
24926             add.render(this.el);
24927             
24928             this.add( add );
24929         }, this);
24930         
24931     },
24932     /**
24933      * Mark fields in this form invalid in bulk.
24934      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
24935      * @return {BasicForm} this
24936      */
24937     markInvalid : function(errors){
24938         if(errors instanceof Array){
24939             for(var i = 0, len = errors.length; i < len; i++){
24940                 var fieldError = errors[i];
24941                 var f = this.findField(fieldError.id);
24942                 if(f){
24943                     f.markInvalid(fieldError.msg);
24944                 }
24945             }
24946         }else{
24947             var field, id;
24948             for(id in errors){
24949                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
24950                     field.markInvalid(errors[id]);
24951                 }
24952             }
24953         }
24954         Roo.each(this.childForms || [], function (f) {
24955             f.markInvalid(errors);
24956         });
24957         
24958         return this;
24959     },
24960
24961     /**
24962      * Set values for fields in this form in bulk.
24963      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
24964      * @return {BasicForm} this
24965      */
24966     setValues : function(values){
24967         if(values instanceof Array){ // array of objects
24968             for(var i = 0, len = values.length; i < len; i++){
24969                 var v = values[i];
24970                 var f = this.findField(v.id);
24971                 if(f){
24972                     f.setValue(v.value);
24973                     if(this.trackResetOnLoad){
24974                         f.originalValue = f.getValue();
24975                     }
24976                 }
24977             }
24978         }else{ // object hash
24979             var field, id;
24980             for(id in values){
24981                 if(typeof values[id] != 'function' && (field = this.findField(id))){
24982                     
24983                     if (field.setFromData && 
24984                         field.valueField && 
24985                         field.displayField &&
24986                         // combos' with local stores can 
24987                         // be queried via setValue()
24988                         // to set their value..
24989                         (field.store && !field.store.isLocal)
24990                         ) {
24991                         // it's a combo
24992                         var sd = { };
24993                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
24994                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
24995                         field.setFromData(sd);
24996                         
24997                     } else {
24998                         field.setValue(values[id]);
24999                     }
25000                     
25001                     
25002                     if(this.trackResetOnLoad){
25003                         field.originalValue = field.getValue();
25004                     }
25005                 }
25006             }
25007         }
25008         this.resetHasChanged();
25009         
25010         
25011         Roo.each(this.childForms || [], function (f) {
25012             f.setValues(values);
25013             f.resetHasChanged();
25014         });
25015                 
25016         return this;
25017     },
25018  
25019     /**
25020      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
25021      * they are returned as an array.
25022      * @param {Boolean} asString
25023      * @return {Object}
25024      */
25025     getValues : function(asString){
25026         if (this.childForms) {
25027             // copy values from the child forms
25028             Roo.each(this.childForms, function (f) {
25029                 this.setValues(f.getValues());
25030             }, this);
25031         }
25032         
25033         // use formdata
25034         if (typeof(FormData) != 'undefined' && asString !== true) {
25035             // this relies on a 'recent' version of chrome apparently...
25036             try {
25037                 var fd = (new FormData(this.el.dom)).entries();
25038                 var ret = {};
25039                 var ent = fd.next();
25040                 while (!ent.done) {
25041                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
25042                     ent = fd.next();
25043                 };
25044                 return ret;
25045             } catch(e) {
25046                 
25047             }
25048             
25049         }
25050         
25051         
25052         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
25053         if(asString === true){
25054             return fs;
25055         }
25056         return Roo.urlDecode(fs);
25057     },
25058     
25059     /**
25060      * Returns the fields in this form as an object with key/value pairs. 
25061      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
25062      * @return {Object}
25063      */
25064     getFieldValues : function(with_hidden)
25065     {
25066         if (this.childForms) {
25067             // copy values from the child forms
25068             // should this call getFieldValues - probably not as we do not currently copy
25069             // hidden fields when we generate..
25070             Roo.each(this.childForms, function (f) {
25071                 this.setValues(f.getValues());
25072             }, this);
25073         }
25074         
25075         var ret = {};
25076         this.items.each(function(f){
25077             if (!f.getName()) {
25078                 return;
25079             }
25080             var v = f.getValue();
25081             if (f.inputType =='radio') {
25082                 if (typeof(ret[f.getName()]) == 'undefined') {
25083                     ret[f.getName()] = ''; // empty..
25084                 }
25085                 
25086                 if (!f.el.dom.checked) {
25087                     return;
25088                     
25089                 }
25090                 v = f.el.dom.value;
25091                 
25092             }
25093             
25094             // not sure if this supported any more..
25095             if ((typeof(v) == 'object') && f.getRawValue) {
25096                 v = f.getRawValue() ; // dates..
25097             }
25098             // combo boxes where name != hiddenName...
25099             if (f.name != f.getName()) {
25100                 ret[f.name] = f.getRawValue();
25101             }
25102             ret[f.getName()] = v;
25103         });
25104         
25105         return ret;
25106     },
25107
25108     /**
25109      * Clears all invalid messages in this form.
25110      * @return {BasicForm} this
25111      */
25112     clearInvalid : function(){
25113         this.items.each(function(f){
25114            f.clearInvalid();
25115         });
25116         
25117         Roo.each(this.childForms || [], function (f) {
25118             f.clearInvalid();
25119         });
25120         
25121         
25122         return this;
25123     },
25124
25125     /**
25126      * Resets this form.
25127      * @return {BasicForm} this
25128      */
25129     reset : function(){
25130         this.items.each(function(f){
25131             f.reset();
25132         });
25133         
25134         Roo.each(this.childForms || [], function (f) {
25135             f.reset();
25136         });
25137         this.resetHasChanged();
25138         
25139         return this;
25140     },
25141
25142     /**
25143      * Add Roo.form components to this form.
25144      * @param {Field} field1
25145      * @param {Field} field2 (optional)
25146      * @param {Field} etc (optional)
25147      * @return {BasicForm} this
25148      */
25149     add : function(){
25150         this.items.addAll(Array.prototype.slice.call(arguments, 0));
25151         return this;
25152     },
25153
25154
25155     /**
25156      * Removes a field from the items collection (does NOT remove its markup).
25157      * @param {Field} field
25158      * @return {BasicForm} this
25159      */
25160     remove : function(field){
25161         this.items.remove(field);
25162         return this;
25163     },
25164
25165     /**
25166      * Looks at the fields in this form, checks them for an id attribute,
25167      * and calls applyTo on the existing dom element with that id.
25168      * @return {BasicForm} this
25169      */
25170     render : function(){
25171         this.items.each(function(f){
25172             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
25173                 f.applyTo(f.id);
25174             }
25175         });
25176         return this;
25177     },
25178
25179     /**
25180      * Calls {@link Ext#apply} for all fields in this form with the passed object.
25181      * @param {Object} values
25182      * @return {BasicForm} this
25183      */
25184     applyToFields : function(o){
25185         this.items.each(function(f){
25186            Roo.apply(f, o);
25187         });
25188         return this;
25189     },
25190
25191     /**
25192      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
25193      * @param {Object} values
25194      * @return {BasicForm} this
25195      */
25196     applyIfToFields : function(o){
25197         this.items.each(function(f){
25198            Roo.applyIf(f, o);
25199         });
25200         return this;
25201     }
25202 });
25203
25204 // back compat
25205 Roo.BasicForm = Roo.form.BasicForm;
25206
25207 Roo.apply(Roo.form.BasicForm, {
25208     
25209     popover : {
25210         
25211         padding : 5,
25212         
25213         isApplied : false,
25214         
25215         isMasked : false,
25216         
25217         form : false,
25218         
25219         target : false,
25220         
25221         intervalID : false,
25222         
25223         maskEl : false,
25224         
25225         apply : function()
25226         {
25227             if(this.isApplied){
25228                 return;
25229             }
25230             
25231             this.maskEl = {
25232                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
25233                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
25234                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
25235                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
25236             };
25237             
25238             this.maskEl.top.enableDisplayMode("block");
25239             this.maskEl.left.enableDisplayMode("block");
25240             this.maskEl.bottom.enableDisplayMode("block");
25241             this.maskEl.right.enableDisplayMode("block");
25242             
25243             Roo.get(document.body).on('click', function(){
25244                 this.unmask();
25245             }, this);
25246             
25247             Roo.get(document.body).on('touchstart', function(){
25248                 this.unmask();
25249             }, this);
25250             
25251             this.isApplied = true
25252         },
25253         
25254         mask : function(form, target)
25255         {
25256             this.form = form;
25257             
25258             this.target = target;
25259             
25260             if(!this.form.errorMask || !target.el){
25261                 return;
25262             }
25263             
25264             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
25265             
25266             var ot = this.target.el.calcOffsetsTo(scrollable);
25267             
25268             var scrollTo = ot[1] - this.form.maskOffset;
25269             
25270             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
25271             
25272             scrollable.scrollTo('top', scrollTo);
25273             
25274             var el = this.target.wrap || this.target.el;
25275             
25276             var box = el.getBox();
25277             
25278             this.maskEl.top.setStyle('position', 'absolute');
25279             this.maskEl.top.setStyle('z-index', 10000);
25280             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
25281             this.maskEl.top.setLeft(0);
25282             this.maskEl.top.setTop(0);
25283             this.maskEl.top.show();
25284             
25285             this.maskEl.left.setStyle('position', 'absolute');
25286             this.maskEl.left.setStyle('z-index', 10000);
25287             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
25288             this.maskEl.left.setLeft(0);
25289             this.maskEl.left.setTop(box.y - this.padding);
25290             this.maskEl.left.show();
25291
25292             this.maskEl.bottom.setStyle('position', 'absolute');
25293             this.maskEl.bottom.setStyle('z-index', 10000);
25294             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
25295             this.maskEl.bottom.setLeft(0);
25296             this.maskEl.bottom.setTop(box.bottom + this.padding);
25297             this.maskEl.bottom.show();
25298
25299             this.maskEl.right.setStyle('position', 'absolute');
25300             this.maskEl.right.setStyle('z-index', 10000);
25301             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
25302             this.maskEl.right.setLeft(box.right + this.padding);
25303             this.maskEl.right.setTop(box.y - this.padding);
25304             this.maskEl.right.show();
25305
25306             this.intervalID = window.setInterval(function() {
25307                 Roo.form.BasicForm.popover.unmask();
25308             }, 10000);
25309
25310             window.onwheel = function(){ return false;};
25311             
25312             (function(){ this.isMasked = true; }).defer(500, this);
25313             
25314         },
25315         
25316         unmask : function()
25317         {
25318             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
25319                 return;
25320             }
25321             
25322             this.maskEl.top.setStyle('position', 'absolute');
25323             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
25324             this.maskEl.top.hide();
25325
25326             this.maskEl.left.setStyle('position', 'absolute');
25327             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
25328             this.maskEl.left.hide();
25329
25330             this.maskEl.bottom.setStyle('position', 'absolute');
25331             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
25332             this.maskEl.bottom.hide();
25333
25334             this.maskEl.right.setStyle('position', 'absolute');
25335             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
25336             this.maskEl.right.hide();
25337             
25338             window.onwheel = function(){ return true;};
25339             
25340             if(this.intervalID){
25341                 window.clearInterval(this.intervalID);
25342                 this.intervalID = false;
25343             }
25344             
25345             this.isMasked = false;
25346             
25347         }
25348         
25349     }
25350     
25351 });/*
25352  * Based on:
25353  * Ext JS Library 1.1.1
25354  * Copyright(c) 2006-2007, Ext JS, LLC.
25355  *
25356  * Originally Released Under LGPL - original licence link has changed is not relivant.
25357  *
25358  * Fork - LGPL
25359  * <script type="text/javascript">
25360  */
25361
25362 /**
25363  * @class Roo.form.Form
25364  * @extends Roo.form.BasicForm
25365  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
25366  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
25367  * @constructor
25368  * @param {Object} config Configuration options
25369  */
25370 Roo.form.Form = function(config){
25371     var xitems =  [];
25372     if (config.items) {
25373         xitems = config.items;
25374         delete config.items;
25375     }
25376    
25377     
25378     Roo.form.Form.superclass.constructor.call(this, null, config);
25379     this.url = this.url || this.action;
25380     if(!this.root){
25381         this.root = new Roo.form.Layout(Roo.applyIf({
25382             id: Roo.id()
25383         }, config));
25384     }
25385     this.active = this.root;
25386     /**
25387      * Array of all the buttons that have been added to this form via {@link addButton}
25388      * @type Array
25389      */
25390     this.buttons = [];
25391     this.allItems = [];
25392     this.addEvents({
25393         /**
25394          * @event clientvalidation
25395          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25396          * @param {Form} this
25397          * @param {Boolean} valid true if the form has passed client-side validation
25398          */
25399         clientvalidation: true,
25400         /**
25401          * @event rendered
25402          * Fires when the form is rendered
25403          * @param {Roo.form.Form} form
25404          */
25405         rendered : true
25406     });
25407     
25408     if (this.progressUrl) {
25409             // push a hidden field onto the list of fields..
25410             this.addxtype( {
25411                     xns: Roo.form, 
25412                     xtype : 'Hidden', 
25413                     name : 'UPLOAD_IDENTIFIER' 
25414             });
25415         }
25416         
25417     
25418     Roo.each(xitems, this.addxtype, this);
25419     
25420 };
25421
25422 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25423      /**
25424      * @cfg {Roo.Button} buttons[] buttons at bottom of form
25425      */
25426     
25427     /**
25428      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25429      */
25430     /**
25431      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25432      */
25433     /**
25434      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25435      */
25436     buttonAlign:'center',
25437
25438     /**
25439      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25440      */
25441     minButtonWidth:75,
25442
25443     /**
25444      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25445      * This property cascades to child containers if not set.
25446      */
25447     labelAlign:'left',
25448
25449     /**
25450      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25451      * fires a looping event with that state. This is required to bind buttons to the valid
25452      * state using the config value formBind:true on the button.
25453      */
25454     monitorValid : false,
25455
25456     /**
25457      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25458      */
25459     monitorPoll : 200,
25460     
25461     /**
25462      * @cfg {String} progressUrl - Url to return progress data 
25463      */
25464     
25465     progressUrl : false,
25466     /**
25467      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25468      * sending a formdata with extra parameters - eg uploaded elements.
25469      */
25470     
25471     formData : false,
25472     
25473     /**
25474      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25475      * fields are added and the column is closed. If no fields are passed the column remains open
25476      * until end() is called.
25477      * @param {Object} config The config to pass to the column
25478      * @param {Field} field1 (optional)
25479      * @param {Field} field2 (optional)
25480      * @param {Field} etc (optional)
25481      * @return Column The column container object
25482      */
25483     column : function(c){
25484         var col = new Roo.form.Column(c);
25485         this.start(col);
25486         if(arguments.length > 1){ // duplicate code required because of Opera
25487             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25488             this.end();
25489         }
25490         return col;
25491     },
25492
25493     /**
25494      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25495      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25496      * until end() is called.
25497      * @param {Object} config The config to pass to the fieldset
25498      * @param {Field} field1 (optional)
25499      * @param {Field} field2 (optional)
25500      * @param {Field} etc (optional)
25501      * @return FieldSet The fieldset container object
25502      */
25503     fieldset : function(c){
25504         var fs = new Roo.form.FieldSet(c);
25505         this.start(fs);
25506         if(arguments.length > 1){ // duplicate code required because of Opera
25507             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25508             this.end();
25509         }
25510         return fs;
25511     },
25512
25513     /**
25514      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25515      * fields are added and the container is closed. If no fields are passed the container remains open
25516      * until end() is called.
25517      * @param {Object} config The config to pass to the Layout
25518      * @param {Field} field1 (optional)
25519      * @param {Field} field2 (optional)
25520      * @param {Field} etc (optional)
25521      * @return Layout The container object
25522      */
25523     container : function(c){
25524         var l = new Roo.form.Layout(c);
25525         this.start(l);
25526         if(arguments.length > 1){ // duplicate code required because of Opera
25527             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25528             this.end();
25529         }
25530         return l;
25531     },
25532
25533     /**
25534      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25535      * @param {Object} container A Roo.form.Layout or subclass of Layout
25536      * @return {Form} this
25537      */
25538     start : function(c){
25539         // cascade label info
25540         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25541         this.active.stack.push(c);
25542         c.ownerCt = this.active;
25543         this.active = c;
25544         return this;
25545     },
25546
25547     /**
25548      * Closes the current open container
25549      * @return {Form} this
25550      */
25551     end : function(){
25552         if(this.active == this.root){
25553             return this;
25554         }
25555         this.active = this.active.ownerCt;
25556         return this;
25557     },
25558
25559     /**
25560      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25561      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25562      * as the label of the field.
25563      * @param {Field} field1
25564      * @param {Field} field2 (optional)
25565      * @param {Field} etc. (optional)
25566      * @return {Form} this
25567      */
25568     add : function(){
25569         this.active.stack.push.apply(this.active.stack, arguments);
25570         this.allItems.push.apply(this.allItems,arguments);
25571         var r = [];
25572         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25573             if(a[i].isFormField){
25574                 r.push(a[i]);
25575             }
25576         }
25577         if(r.length > 0){
25578             Roo.form.Form.superclass.add.apply(this, r);
25579         }
25580         return this;
25581     },
25582     
25583
25584     
25585     
25586     
25587      /**
25588      * Find any element that has been added to a form, using it's ID or name
25589      * This can include framesets, columns etc. along with regular fields..
25590      * @param {String} id - id or name to find.
25591      
25592      * @return {Element} e - or false if nothing found.
25593      */
25594     findbyId : function(id)
25595     {
25596         var ret = false;
25597         if (!id) {
25598             return ret;
25599         }
25600         Roo.each(this.allItems, function(f){
25601             if (f.id == id || f.name == id ){
25602                 ret = f;
25603                 return false;
25604             }
25605         });
25606         return ret;
25607     },
25608
25609     
25610     
25611     /**
25612      * Render this form into the passed container. This should only be called once!
25613      * @param {String/HTMLElement/Element} container The element this component should be rendered into
25614      * @return {Form} this
25615      */
25616     render : function(ct)
25617     {
25618         
25619         
25620         
25621         ct = Roo.get(ct);
25622         var o = this.autoCreate || {
25623             tag: 'form',
25624             method : this.method || 'POST',
25625             id : this.id || Roo.id()
25626         };
25627         this.initEl(ct.createChild(o));
25628
25629         this.root.render(this.el);
25630         
25631        
25632              
25633         this.items.each(function(f){
25634             f.render('x-form-el-'+f.id);
25635         });
25636
25637         if(this.buttons.length > 0){
25638             // tables are required to maintain order and for correct IE layout
25639             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25640                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25641                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25642             }}, null, true);
25643             var tr = tb.getElementsByTagName('tr')[0];
25644             for(var i = 0, len = this.buttons.length; i < len; i++) {
25645                 var b = this.buttons[i];
25646                 var td = document.createElement('td');
25647                 td.className = 'x-form-btn-td';
25648                 b.render(tr.appendChild(td));
25649             }
25650         }
25651         if(this.monitorValid){ // initialize after render
25652             this.startMonitoring();
25653         }
25654         this.fireEvent('rendered', this);
25655         return this;
25656     },
25657
25658     /**
25659      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25660      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25661      * object or a valid Roo.DomHelper element config
25662      * @param {Function} handler The function called when the button is clicked
25663      * @param {Object} scope (optional) The scope of the handler function
25664      * @return {Roo.Button}
25665      */
25666     addButton : function(config, handler, scope){
25667         var bc = {
25668             handler: handler,
25669             scope: scope,
25670             minWidth: this.minButtonWidth,
25671             hideParent:true
25672         };
25673         if(typeof config == "string"){
25674             bc.text = config;
25675         }else{
25676             Roo.apply(bc, config);
25677         }
25678         var btn = new Roo.Button(null, bc);
25679         this.buttons.push(btn);
25680         return btn;
25681     },
25682
25683      /**
25684      * Adds a series of form elements (using the xtype property as the factory method.
25685      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25686      * @param {Object} config 
25687      */
25688     
25689     addxtype : function()
25690     {
25691         var ar = Array.prototype.slice.call(arguments, 0);
25692         var ret = false;
25693         for(var i = 0; i < ar.length; i++) {
25694             if (!ar[i]) {
25695                 continue; // skip -- if this happends something invalid got sent, we 
25696                 // should ignore it, as basically that interface element will not show up
25697                 // and that should be pretty obvious!!
25698             }
25699             
25700             if (Roo.form[ar[i].xtype]) {
25701                 ar[i].form = this;
25702                 var fe = Roo.factory(ar[i], Roo.form);
25703                 if (!ret) {
25704                     ret = fe;
25705                 }
25706                 fe.form = this;
25707                 if (fe.store) {
25708                     fe.store.form = this;
25709                 }
25710                 if (fe.isLayout) {  
25711                          
25712                     this.start(fe);
25713                     this.allItems.push(fe);
25714                     if (fe.items && fe.addxtype) {
25715                         fe.addxtype.apply(fe, fe.items);
25716                         delete fe.items;
25717                     }
25718                      this.end();
25719                     continue;
25720                 }
25721                 
25722                 
25723                  
25724                 this.add(fe);
25725               //  console.log('adding ' + ar[i].xtype);
25726             }
25727             if (ar[i].xtype == 'Button') {  
25728                 //console.log('adding button');
25729                 //console.log(ar[i]);
25730                 this.addButton(ar[i]);
25731                 this.allItems.push(fe);
25732                 continue;
25733             }
25734             
25735             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25736                 alert('end is not supported on xtype any more, use items');
25737             //    this.end();
25738             //    //console.log('adding end');
25739             }
25740             
25741         }
25742         return ret;
25743     },
25744     
25745     /**
25746      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
25747      * option "monitorValid"
25748      */
25749     startMonitoring : function(){
25750         if(!this.bound){
25751             this.bound = true;
25752             Roo.TaskMgr.start({
25753                 run : this.bindHandler,
25754                 interval : this.monitorPoll || 200,
25755                 scope: this
25756             });
25757         }
25758     },
25759
25760     /**
25761      * Stops monitoring of the valid state of this form
25762      */
25763     stopMonitoring : function(){
25764         this.bound = false;
25765     },
25766
25767     // private
25768     bindHandler : function(){
25769         if(!this.bound){
25770             return false; // stops binding
25771         }
25772         var valid = true;
25773         this.items.each(function(f){
25774             if(!f.isValid(true)){
25775                 valid = false;
25776                 return false;
25777             }
25778         });
25779         for(var i = 0, len = this.buttons.length; i < len; i++){
25780             var btn = this.buttons[i];
25781             if(btn.formBind === true && btn.disabled === valid){
25782                 btn.setDisabled(!valid);
25783             }
25784         }
25785         this.fireEvent('clientvalidation', this, valid);
25786     }
25787     
25788     
25789     
25790     
25791     
25792     
25793     
25794     
25795 });
25796
25797
25798 // back compat
25799 Roo.Form = Roo.form.Form;
25800 /*
25801  * Based on:
25802  * Ext JS Library 1.1.1
25803  * Copyright(c) 2006-2007, Ext JS, LLC.
25804  *
25805  * Originally Released Under LGPL - original licence link has changed is not relivant.
25806  *
25807  * Fork - LGPL
25808  * <script type="text/javascript">
25809  */
25810
25811 // as we use this in bootstrap.
25812 Roo.namespace('Roo.form');
25813  /**
25814  * @class Roo.form.Action
25815  * Internal Class used to handle form actions
25816  * @constructor
25817  * @param {Roo.form.BasicForm} el The form element or its id
25818  * @param {Object} config Configuration options
25819  */
25820
25821  
25822  
25823 // define the action interface
25824 Roo.form.Action = function(form, options){
25825     this.form = form;
25826     this.options = options || {};
25827 };
25828 /**
25829  * Client Validation Failed
25830  * @const 
25831  */
25832 Roo.form.Action.CLIENT_INVALID = 'client';
25833 /**
25834  * Server Validation Failed
25835  * @const 
25836  */
25837 Roo.form.Action.SERVER_INVALID = 'server';
25838  /**
25839  * Connect to Server Failed
25840  * @const 
25841  */
25842 Roo.form.Action.CONNECT_FAILURE = 'connect';
25843 /**
25844  * Reading Data from Server Failed
25845  * @const 
25846  */
25847 Roo.form.Action.LOAD_FAILURE = 'load';
25848
25849 Roo.form.Action.prototype = {
25850     type : 'default',
25851     failureType : undefined,
25852     response : undefined,
25853     result : undefined,
25854
25855     // interface method
25856     run : function(options){
25857
25858     },
25859
25860     // interface method
25861     success : function(response){
25862
25863     },
25864
25865     // interface method
25866     handleResponse : function(response){
25867
25868     },
25869
25870     // default connection failure
25871     failure : function(response){
25872         
25873         this.response = response;
25874         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25875         this.form.afterAction(this, false);
25876     },
25877
25878     processResponse : function(response){
25879         this.response = response;
25880         if(!response.responseText){
25881             return true;
25882         }
25883         this.result = this.handleResponse(response);
25884         return this.result;
25885     },
25886
25887     // utility functions used internally
25888     getUrl : function(appendParams){
25889         var url = this.options.url || this.form.url || this.form.el.dom.action;
25890         if(appendParams){
25891             var p = this.getParams();
25892             if(p){
25893                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
25894             }
25895         }
25896         return url;
25897     },
25898
25899     getMethod : function(){
25900         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
25901     },
25902
25903     getParams : function(){
25904         var bp = this.form.baseParams;
25905         var p = this.options.params;
25906         if(p){
25907             if(typeof p == "object"){
25908                 p = Roo.urlEncode(Roo.applyIf(p, bp));
25909             }else if(typeof p == 'string' && bp){
25910                 p += '&' + Roo.urlEncode(bp);
25911             }
25912         }else if(bp){
25913             p = Roo.urlEncode(bp);
25914         }
25915         return p;
25916     },
25917
25918     createCallback : function(){
25919         return {
25920             success: this.success,
25921             failure: this.failure,
25922             scope: this,
25923             timeout: (this.form.timeout*1000),
25924             upload: this.form.fileUpload ? this.success : undefined
25925         };
25926     }
25927 };
25928
25929 Roo.form.Action.Submit = function(form, options){
25930     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
25931 };
25932
25933 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
25934     type : 'submit',
25935
25936     haveProgress : false,
25937     uploadComplete : false,
25938     
25939     // uploadProgress indicator.
25940     uploadProgress : function()
25941     {
25942         if (!this.form.progressUrl) {
25943             return;
25944         }
25945         
25946         if (!this.haveProgress) {
25947             Roo.MessageBox.progress("Uploading", "Uploading");
25948         }
25949         if (this.uploadComplete) {
25950            Roo.MessageBox.hide();
25951            return;
25952         }
25953         
25954         this.haveProgress = true;
25955    
25956         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
25957         
25958         var c = new Roo.data.Connection();
25959         c.request({
25960             url : this.form.progressUrl,
25961             params: {
25962                 id : uid
25963             },
25964             method: 'GET',
25965             success : function(req){
25966                //console.log(data);
25967                 var rdata = false;
25968                 var edata;
25969                 try  {
25970                    rdata = Roo.decode(req.responseText)
25971                 } catch (e) {
25972                     Roo.log("Invalid data from server..");
25973                     Roo.log(edata);
25974                     return;
25975                 }
25976                 if (!rdata || !rdata.success) {
25977                     Roo.log(rdata);
25978                     Roo.MessageBox.alert(Roo.encode(rdata));
25979                     return;
25980                 }
25981                 var data = rdata.data;
25982                 
25983                 if (this.uploadComplete) {
25984                    Roo.MessageBox.hide();
25985                    return;
25986                 }
25987                    
25988                 if (data){
25989                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
25990                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
25991                     );
25992                 }
25993                 this.uploadProgress.defer(2000,this);
25994             },
25995        
25996             failure: function(data) {
25997                 Roo.log('progress url failed ');
25998                 Roo.log(data);
25999             },
26000             scope : this
26001         });
26002            
26003     },
26004     
26005     
26006     run : function()
26007     {
26008         // run get Values on the form, so it syncs any secondary forms.
26009         this.form.getValues();
26010         
26011         var o = this.options;
26012         var method = this.getMethod();
26013         var isPost = method == 'POST';
26014         if(o.clientValidation === false || this.form.isValid()){
26015             
26016             if (this.form.progressUrl) {
26017                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
26018                     (new Date() * 1) + '' + Math.random());
26019                     
26020             } 
26021             
26022             
26023             Roo.Ajax.request(Roo.apply(this.createCallback(), {
26024                 form:this.form.el.dom,
26025                 url:this.getUrl(!isPost),
26026                 method: method,
26027                 params:isPost ? this.getParams() : null,
26028                 isUpload: this.form.fileUpload,
26029                 formData : this.form.formData
26030             }));
26031             
26032             this.uploadProgress();
26033
26034         }else if (o.clientValidation !== false){ // client validation failed
26035             this.failureType = Roo.form.Action.CLIENT_INVALID;
26036             this.form.afterAction(this, false);
26037         }
26038     },
26039
26040     success : function(response)
26041     {
26042         this.uploadComplete= true;
26043         if (this.haveProgress) {
26044             Roo.MessageBox.hide();
26045         }
26046         
26047         
26048         var result = this.processResponse(response);
26049         if(result === true || result.success){
26050             this.form.afterAction(this, true);
26051             return;
26052         }
26053         if(result.errors){
26054             this.form.markInvalid(result.errors);
26055             this.failureType = Roo.form.Action.SERVER_INVALID;
26056         }
26057         this.form.afterAction(this, false);
26058     },
26059     failure : function(response)
26060     {
26061         this.uploadComplete= true;
26062         if (this.haveProgress) {
26063             Roo.MessageBox.hide();
26064         }
26065         
26066         this.response = response;
26067         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26068         this.form.afterAction(this, false);
26069     },
26070     
26071     handleResponse : function(response){
26072         if(this.form.errorReader){
26073             var rs = this.form.errorReader.read(response);
26074             var errors = [];
26075             if(rs.records){
26076                 for(var i = 0, len = rs.records.length; i < len; i++) {
26077                     var r = rs.records[i];
26078                     errors[i] = r.data;
26079                 }
26080             }
26081             if(errors.length < 1){
26082                 errors = null;
26083             }
26084             return {
26085                 success : rs.success,
26086                 errors : errors
26087             };
26088         }
26089         var ret = false;
26090         try {
26091             ret = Roo.decode(response.responseText);
26092         } catch (e) {
26093             ret = {
26094                 success: false,
26095                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
26096                 errors : []
26097             };
26098         }
26099         return ret;
26100         
26101     }
26102 });
26103
26104
26105 Roo.form.Action.Load = function(form, options){
26106     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
26107     this.reader = this.form.reader;
26108 };
26109
26110 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
26111     type : 'load',
26112
26113     run : function(){
26114         
26115         Roo.Ajax.request(Roo.apply(
26116                 this.createCallback(), {
26117                     method:this.getMethod(),
26118                     url:this.getUrl(false),
26119                     params:this.getParams()
26120         }));
26121     },
26122
26123     success : function(response){
26124         
26125         var result = this.processResponse(response);
26126         if(result === true || !result.success || !result.data){
26127             this.failureType = Roo.form.Action.LOAD_FAILURE;
26128             this.form.afterAction(this, false);
26129             return;
26130         }
26131         this.form.clearInvalid();
26132         this.form.setValues(result.data);
26133         this.form.afterAction(this, true);
26134     },
26135
26136     handleResponse : function(response){
26137         if(this.form.reader){
26138             var rs = this.form.reader.read(response);
26139             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
26140             return {
26141                 success : rs.success,
26142                 data : data
26143             };
26144         }
26145         return Roo.decode(response.responseText);
26146     }
26147 });
26148
26149 Roo.form.Action.ACTION_TYPES = {
26150     'load' : Roo.form.Action.Load,
26151     'submit' : Roo.form.Action.Submit
26152 };/*
26153  * Based on:
26154  * Ext JS Library 1.1.1
26155  * Copyright(c) 2006-2007, Ext JS, LLC.
26156  *
26157  * Originally Released Under LGPL - original licence link has changed is not relivant.
26158  *
26159  * Fork - LGPL
26160  * <script type="text/javascript">
26161  */
26162  
26163 /**
26164  * @class Roo.form.Layout
26165  * @extends Roo.Component
26166  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26167  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
26168  * @constructor
26169  * @param {Object} config Configuration options
26170  */
26171 Roo.form.Layout = function(config){
26172     var xitems = [];
26173     if (config.items) {
26174         xitems = config.items;
26175         delete config.items;
26176     }
26177     Roo.form.Layout.superclass.constructor.call(this, config);
26178     this.stack = [];
26179     Roo.each(xitems, this.addxtype, this);
26180      
26181 };
26182
26183 Roo.extend(Roo.form.Layout, Roo.Component, {
26184     /**
26185      * @cfg {String/Object} autoCreate
26186      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
26187      */
26188     /**
26189      * @cfg {String/Object/Function} style
26190      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
26191      * a function which returns such a specification.
26192      */
26193     /**
26194      * @cfg {String} labelAlign
26195      * Valid values are "left," "top" and "right" (defaults to "left")
26196      */
26197     /**
26198      * @cfg {Number} labelWidth
26199      * Fixed width in pixels of all field labels (defaults to undefined)
26200      */
26201     /**
26202      * @cfg {Boolean} clear
26203      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
26204      */
26205     clear : true,
26206     /**
26207      * @cfg {String} labelSeparator
26208      * The separator to use after field labels (defaults to ':')
26209      */
26210     labelSeparator : ':',
26211     /**
26212      * @cfg {Boolean} hideLabels
26213      * True to suppress the display of field labels in this layout (defaults to false)
26214      */
26215     hideLabels : false,
26216
26217     // private
26218     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
26219     
26220     isLayout : true,
26221     
26222     // private
26223     onRender : function(ct, position){
26224         if(this.el){ // from markup
26225             this.el = Roo.get(this.el);
26226         }else {  // generate
26227             var cfg = this.getAutoCreate();
26228             this.el = ct.createChild(cfg, position);
26229         }
26230         if(this.style){
26231             this.el.applyStyles(this.style);
26232         }
26233         if(this.labelAlign){
26234             this.el.addClass('x-form-label-'+this.labelAlign);
26235         }
26236         if(this.hideLabels){
26237             this.labelStyle = "display:none";
26238             this.elementStyle = "padding-left:0;";
26239         }else{
26240             if(typeof this.labelWidth == 'number'){
26241                 this.labelStyle = "width:"+this.labelWidth+"px;";
26242                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
26243             }
26244             if(this.labelAlign == 'top'){
26245                 this.labelStyle = "width:auto;";
26246                 this.elementStyle = "padding-left:0;";
26247             }
26248         }
26249         var stack = this.stack;
26250         var slen = stack.length;
26251         if(slen > 0){
26252             if(!this.fieldTpl){
26253                 var t = new Roo.Template(
26254                     '<div class="x-form-item {5}">',
26255                         '<label for="{0}" style="{2}">{1}{4}</label>',
26256                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26257                         '</div>',
26258                     '</div><div class="x-form-clear-left"></div>'
26259                 );
26260                 t.disableFormats = true;
26261                 t.compile();
26262                 Roo.form.Layout.prototype.fieldTpl = t;
26263             }
26264             for(var i = 0; i < slen; i++) {
26265                 if(stack[i].isFormField){
26266                     this.renderField(stack[i]);
26267                 }else{
26268                     this.renderComponent(stack[i]);
26269                 }
26270             }
26271         }
26272         if(this.clear){
26273             this.el.createChild({cls:'x-form-clear'});
26274         }
26275     },
26276
26277     // private
26278     renderField : function(f){
26279         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
26280                f.id, //0
26281                f.fieldLabel, //1
26282                f.labelStyle||this.labelStyle||'', //2
26283                this.elementStyle||'', //3
26284                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
26285                f.itemCls||this.itemCls||''  //5
26286        ], true).getPrevSibling());
26287     },
26288
26289     // private
26290     renderComponent : function(c){
26291         c.render(c.isLayout ? this.el : this.el.createChild());    
26292     },
26293     /**
26294      * Adds a object form elements (using the xtype property as the factory method.)
26295      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
26296      * @param {Object} config 
26297      */
26298     addxtype : function(o)
26299     {
26300         // create the lement.
26301         o.form = this.form;
26302         var fe = Roo.factory(o, Roo.form);
26303         this.form.allItems.push(fe);
26304         this.stack.push(fe);
26305         
26306         if (fe.isFormField) {
26307             this.form.items.add(fe);
26308         }
26309          
26310         return fe;
26311     }
26312 });
26313
26314 /**
26315  * @class Roo.form.Column
26316  * @extends Roo.form.Layout
26317  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
26318  * @constructor
26319  * @param {Object} config Configuration options
26320  */
26321 Roo.form.Column = function(config){
26322     Roo.form.Column.superclass.constructor.call(this, config);
26323 };
26324
26325 Roo.extend(Roo.form.Column, Roo.form.Layout, {
26326     /**
26327      * @cfg {Number/String} width
26328      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26329      */
26330     /**
26331      * @cfg {String/Object} autoCreate
26332      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
26333      */
26334
26335     // private
26336     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
26337
26338     // private
26339     onRender : function(ct, position){
26340         Roo.form.Column.superclass.onRender.call(this, ct, position);
26341         if(this.width){
26342             this.el.setWidth(this.width);
26343         }
26344     }
26345 });
26346
26347
26348 /**
26349  * @class Roo.form.Row
26350  * @extends Roo.form.Layout
26351  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26352  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
26353  * @constructor
26354  * @param {Object} config Configuration options
26355  */
26356
26357  
26358 Roo.form.Row = function(config){
26359     Roo.form.Row.superclass.constructor.call(this, config);
26360 };
26361  
26362 Roo.extend(Roo.form.Row, Roo.form.Layout, {
26363       /**
26364      * @cfg {Number/String} width
26365      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26366      */
26367     /**
26368      * @cfg {Number/String} height
26369      * The fixed height of the column in pixels or CSS value (defaults to "auto")
26370      */
26371     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
26372     
26373     padWidth : 20,
26374     // private
26375     onRender : function(ct, position){
26376         //console.log('row render');
26377         if(!this.rowTpl){
26378             var t = new Roo.Template(
26379                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
26380                     '<label for="{0}" style="{2}">{1}{4}</label>',
26381                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26382                     '</div>',
26383                 '</div>'
26384             );
26385             t.disableFormats = true;
26386             t.compile();
26387             Roo.form.Layout.prototype.rowTpl = t;
26388         }
26389         this.fieldTpl = this.rowTpl;
26390         
26391         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26392         var labelWidth = 100;
26393         
26394         if ((this.labelAlign != 'top')) {
26395             if (typeof this.labelWidth == 'number') {
26396                 labelWidth = this.labelWidth
26397             }
26398             this.padWidth =  20 + labelWidth;
26399             
26400         }
26401         
26402         Roo.form.Column.superclass.onRender.call(this, ct, position);
26403         if(this.width){
26404             this.el.setWidth(this.width);
26405         }
26406         if(this.height){
26407             this.el.setHeight(this.height);
26408         }
26409     },
26410     
26411     // private
26412     renderField : function(f){
26413         f.fieldEl = this.fieldTpl.append(this.el, [
26414                f.id, f.fieldLabel,
26415                f.labelStyle||this.labelStyle||'',
26416                this.elementStyle||'',
26417                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26418                f.itemCls||this.itemCls||'',
26419                f.width ? f.width + this.padWidth : 160 + this.padWidth
26420        ],true);
26421     }
26422 });
26423  
26424
26425 /**
26426  * @class Roo.form.FieldSet
26427  * @extends Roo.form.Layout
26428  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26429  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26430  * @constructor
26431  * @param {Object} config Configuration options
26432  */
26433 Roo.form.FieldSet = function(config){
26434     Roo.form.FieldSet.superclass.constructor.call(this, config);
26435 };
26436
26437 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26438     /**
26439      * @cfg {String} legend
26440      * The text to display as the legend for the FieldSet (defaults to '')
26441      */
26442     /**
26443      * @cfg {String/Object} autoCreate
26444      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26445      */
26446
26447     // private
26448     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26449
26450     // private
26451     onRender : function(ct, position){
26452         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26453         if(this.legend){
26454             this.setLegend(this.legend);
26455         }
26456     },
26457
26458     // private
26459     setLegend : function(text){
26460         if(this.rendered){
26461             this.el.child('legend').update(text);
26462         }
26463     }
26464 });/*
26465  * Based on:
26466  * Ext JS Library 1.1.1
26467  * Copyright(c) 2006-2007, Ext JS, LLC.
26468  *
26469  * Originally Released Under LGPL - original licence link has changed is not relivant.
26470  *
26471  * Fork - LGPL
26472  * <script type="text/javascript">
26473  */
26474 /**
26475  * @class Roo.form.VTypes
26476  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26477  * @static
26478  */
26479 Roo.form.VTypes = function(){
26480     // closure these in so they are only created once.
26481     var alpha = /^[a-zA-Z_]+$/;
26482     var alphanum = /^[a-zA-Z0-9_]+$/;
26483     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26484     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26485
26486     // All these messages and functions are configurable
26487     return {
26488         /**
26489          * The function used to validate email addresses
26490          * @param {String} value The email address
26491          */
26492         'email' : function(v){
26493             return email.test(v);
26494         },
26495         /**
26496          * The error text to display when the email validation function returns false
26497          * @type String
26498          */
26499         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26500         /**
26501          * The keystroke filter mask to be applied on email input
26502          * @type RegExp
26503          */
26504         'emailMask' : /[a-z0-9_\.\-@]/i,
26505
26506         /**
26507          * The function used to validate URLs
26508          * @param {String} value The URL
26509          */
26510         'url' : function(v){
26511             return url.test(v);
26512         },
26513         /**
26514          * The error text to display when the url validation function returns false
26515          * @type String
26516          */
26517         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26518         
26519         /**
26520          * The function used to validate alpha values
26521          * @param {String} value The value
26522          */
26523         'alpha' : function(v){
26524             return alpha.test(v);
26525         },
26526         /**
26527          * The error text to display when the alpha validation function returns false
26528          * @type String
26529          */
26530         'alphaText' : 'This field should only contain letters and _',
26531         /**
26532          * The keystroke filter mask to be applied on alpha input
26533          * @type RegExp
26534          */
26535         'alphaMask' : /[a-z_]/i,
26536
26537         /**
26538          * The function used to validate alphanumeric values
26539          * @param {String} value The value
26540          */
26541         'alphanum' : function(v){
26542             return alphanum.test(v);
26543         },
26544         /**
26545          * The error text to display when the alphanumeric validation function returns false
26546          * @type String
26547          */
26548         'alphanumText' : 'This field should only contain letters, numbers and _',
26549         /**
26550          * The keystroke filter mask to be applied on alphanumeric input
26551          * @type RegExp
26552          */
26553         'alphanumMask' : /[a-z0-9_]/i
26554     };
26555 }();//<script type="text/javascript">
26556
26557 /**
26558  * @class Roo.form.FCKeditor
26559  * @extends Roo.form.TextArea
26560  * Wrapper around the FCKEditor http://www.fckeditor.net
26561  * @constructor
26562  * Creates a new FCKeditor
26563  * @param {Object} config Configuration options
26564  */
26565 Roo.form.FCKeditor = function(config){
26566     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26567     this.addEvents({
26568          /**
26569          * @event editorinit
26570          * Fired when the editor is initialized - you can add extra handlers here..
26571          * @param {FCKeditor} this
26572          * @param {Object} the FCK object.
26573          */
26574         editorinit : true
26575     });
26576     
26577     
26578 };
26579 Roo.form.FCKeditor.editors = { };
26580 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26581 {
26582     //defaultAutoCreate : {
26583     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26584     //},
26585     // private
26586     /**
26587      * @cfg {Object} fck options - see fck manual for details.
26588      */
26589     fckconfig : false,
26590     
26591     /**
26592      * @cfg {Object} fck toolbar set (Basic or Default)
26593      */
26594     toolbarSet : 'Basic',
26595     /**
26596      * @cfg {Object} fck BasePath
26597      */ 
26598     basePath : '/fckeditor/',
26599     
26600     
26601     frame : false,
26602     
26603     value : '',
26604     
26605    
26606     onRender : function(ct, position)
26607     {
26608         if(!this.el){
26609             this.defaultAutoCreate = {
26610                 tag: "textarea",
26611                 style:"width:300px;height:60px;",
26612                 autocomplete: "new-password"
26613             };
26614         }
26615         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26616         /*
26617         if(this.grow){
26618             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26619             if(this.preventScrollbars){
26620                 this.el.setStyle("overflow", "hidden");
26621             }
26622             this.el.setHeight(this.growMin);
26623         }
26624         */
26625         //console.log('onrender' + this.getId() );
26626         Roo.form.FCKeditor.editors[this.getId()] = this;
26627          
26628
26629         this.replaceTextarea() ;
26630         
26631     },
26632     
26633     getEditor : function() {
26634         return this.fckEditor;
26635     },
26636     /**
26637      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26638      * @param {Mixed} value The value to set
26639      */
26640     
26641     
26642     setValue : function(value)
26643     {
26644         //console.log('setValue: ' + value);
26645         
26646         if(typeof(value) == 'undefined') { // not sure why this is happending...
26647             return;
26648         }
26649         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26650         
26651         //if(!this.el || !this.getEditor()) {
26652         //    this.value = value;
26653             //this.setValue.defer(100,this,[value]);    
26654         //    return;
26655         //} 
26656         
26657         if(!this.getEditor()) {
26658             return;
26659         }
26660         
26661         this.getEditor().SetData(value);
26662         
26663         //
26664
26665     },
26666
26667     /**
26668      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26669      * @return {Mixed} value The field value
26670      */
26671     getValue : function()
26672     {
26673         
26674         if (this.frame && this.frame.dom.style.display == 'none') {
26675             return Roo.form.FCKeditor.superclass.getValue.call(this);
26676         }
26677         
26678         if(!this.el || !this.getEditor()) {
26679            
26680            // this.getValue.defer(100,this); 
26681             return this.value;
26682         }
26683        
26684         
26685         var value=this.getEditor().GetData();
26686         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26687         return Roo.form.FCKeditor.superclass.getValue.call(this);
26688         
26689
26690     },
26691
26692     /**
26693      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26694      * @return {Mixed} value The field value
26695      */
26696     getRawValue : function()
26697     {
26698         if (this.frame && this.frame.dom.style.display == 'none') {
26699             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26700         }
26701         
26702         if(!this.el || !this.getEditor()) {
26703             //this.getRawValue.defer(100,this); 
26704             return this.value;
26705             return;
26706         }
26707         
26708         
26709         
26710         var value=this.getEditor().GetData();
26711         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26712         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26713          
26714     },
26715     
26716     setSize : function(w,h) {
26717         
26718         
26719         
26720         //if (this.frame && this.frame.dom.style.display == 'none') {
26721         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26722         //    return;
26723         //}
26724         //if(!this.el || !this.getEditor()) {
26725         //    this.setSize.defer(100,this, [w,h]); 
26726         //    return;
26727         //}
26728         
26729         
26730         
26731         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26732         
26733         this.frame.dom.setAttribute('width', w);
26734         this.frame.dom.setAttribute('height', h);
26735         this.frame.setSize(w,h);
26736         
26737     },
26738     
26739     toggleSourceEdit : function(value) {
26740         
26741       
26742          
26743         this.el.dom.style.display = value ? '' : 'none';
26744         this.frame.dom.style.display = value ?  'none' : '';
26745         
26746     },
26747     
26748     
26749     focus: function(tag)
26750     {
26751         if (this.frame.dom.style.display == 'none') {
26752             return Roo.form.FCKeditor.superclass.focus.call(this);
26753         }
26754         if(!this.el || !this.getEditor()) {
26755             this.focus.defer(100,this, [tag]); 
26756             return;
26757         }
26758         
26759         
26760         
26761         
26762         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
26763         this.getEditor().Focus();
26764         if (tgs.length) {
26765             if (!this.getEditor().Selection.GetSelection()) {
26766                 this.focus.defer(100,this, [tag]); 
26767                 return;
26768             }
26769             
26770             
26771             var r = this.getEditor().EditorDocument.createRange();
26772             r.setStart(tgs[0],0);
26773             r.setEnd(tgs[0],0);
26774             this.getEditor().Selection.GetSelection().removeAllRanges();
26775             this.getEditor().Selection.GetSelection().addRange(r);
26776             this.getEditor().Focus();
26777         }
26778         
26779     },
26780     
26781     
26782     
26783     replaceTextarea : function()
26784     {
26785         if ( document.getElementById( this.getId() + '___Frame' ) ) {
26786             return ;
26787         }
26788         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
26789         //{
26790             // We must check the elements firstly using the Id and then the name.
26791         var oTextarea = document.getElementById( this.getId() );
26792         
26793         var colElementsByName = document.getElementsByName( this.getId() ) ;
26794          
26795         oTextarea.style.display = 'none' ;
26796
26797         if ( oTextarea.tabIndex ) {            
26798             this.TabIndex = oTextarea.tabIndex ;
26799         }
26800         
26801         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
26802         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
26803         this.frame = Roo.get(this.getId() + '___Frame')
26804     },
26805     
26806     _getConfigHtml : function()
26807     {
26808         var sConfig = '' ;
26809
26810         for ( var o in this.fckconfig ) {
26811             sConfig += sConfig.length > 0  ? '&amp;' : '';
26812             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
26813         }
26814
26815         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
26816     },
26817     
26818     
26819     _getIFrameHtml : function()
26820     {
26821         var sFile = 'fckeditor.html' ;
26822         /* no idea what this is about..
26823         try
26824         {
26825             if ( (/fcksource=true/i).test( window.top.location.search ) )
26826                 sFile = 'fckeditor.original.html' ;
26827         }
26828         catch (e) { 
26829         */
26830
26831         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
26832         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
26833         
26834         
26835         var html = '<iframe id="' + this.getId() +
26836             '___Frame" src="' + sLink +
26837             '" width="' + this.width +
26838             '" height="' + this.height + '"' +
26839             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
26840             ' frameborder="0" scrolling="no"></iframe>' ;
26841
26842         return html ;
26843     },
26844     
26845     _insertHtmlBefore : function( html, element )
26846     {
26847         if ( element.insertAdjacentHTML )       {
26848             // IE
26849             element.insertAdjacentHTML( 'beforeBegin', html ) ;
26850         } else { // Gecko
26851             var oRange = document.createRange() ;
26852             oRange.setStartBefore( element ) ;
26853             var oFragment = oRange.createContextualFragment( html );
26854             element.parentNode.insertBefore( oFragment, element ) ;
26855         }
26856     }
26857     
26858     
26859   
26860     
26861     
26862     
26863     
26864
26865 });
26866
26867 //Roo.reg('fckeditor', Roo.form.FCKeditor);
26868
26869 function FCKeditor_OnComplete(editorInstance){
26870     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
26871     f.fckEditor = editorInstance;
26872     //console.log("loaded");
26873     f.fireEvent('editorinit', f, editorInstance);
26874
26875   
26876
26877  
26878
26879
26880
26881
26882
26883
26884
26885
26886
26887
26888
26889
26890
26891
26892
26893 //<script type="text/javascript">
26894 /**
26895  * @class Roo.form.GridField
26896  * @extends Roo.form.Field
26897  * Embed a grid (or editable grid into a form)
26898  * STATUS ALPHA
26899  * 
26900  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
26901  * it needs 
26902  * xgrid.store = Roo.data.Store
26903  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
26904  * xgrid.store.reader = Roo.data.JsonReader 
26905  * 
26906  * 
26907  * @constructor
26908  * Creates a new GridField
26909  * @param {Object} config Configuration options
26910  */
26911 Roo.form.GridField = function(config){
26912     Roo.form.GridField.superclass.constructor.call(this, config);
26913      
26914 };
26915
26916 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
26917     /**
26918      * @cfg {Number} width  - used to restrict width of grid..
26919      */
26920     width : 100,
26921     /**
26922      * @cfg {Number} height - used to restrict height of grid..
26923      */
26924     height : 50,
26925      /**
26926      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
26927          * 
26928          *}
26929      */
26930     xgrid : false, 
26931     /**
26932      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26933      * {tag: "input", type: "checkbox", autocomplete: "off"})
26934      */
26935    // defaultAutoCreate : { tag: 'div' },
26936     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
26937     /**
26938      * @cfg {String} addTitle Text to include for adding a title.
26939      */
26940     addTitle : false,
26941     //
26942     onResize : function(){
26943         Roo.form.Field.superclass.onResize.apply(this, arguments);
26944     },
26945
26946     initEvents : function(){
26947         // Roo.form.Checkbox.superclass.initEvents.call(this);
26948         // has no events...
26949        
26950     },
26951
26952
26953     getResizeEl : function(){
26954         return this.wrap;
26955     },
26956
26957     getPositionEl : function(){
26958         return this.wrap;
26959     },
26960
26961     // private
26962     onRender : function(ct, position){
26963         
26964         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
26965         var style = this.style;
26966         delete this.style;
26967         
26968         Roo.form.GridField.superclass.onRender.call(this, ct, position);
26969         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
26970         this.viewEl = this.wrap.createChild({ tag: 'div' });
26971         if (style) {
26972             this.viewEl.applyStyles(style);
26973         }
26974         if (this.width) {
26975             this.viewEl.setWidth(this.width);
26976         }
26977         if (this.height) {
26978             this.viewEl.setHeight(this.height);
26979         }
26980         //if(this.inputValue !== undefined){
26981         //this.setValue(this.value);
26982         
26983         
26984         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
26985         
26986         
26987         this.grid.render();
26988         this.grid.getDataSource().on('remove', this.refreshValue, this);
26989         this.grid.getDataSource().on('update', this.refreshValue, this);
26990         this.grid.on('afteredit', this.refreshValue, this);
26991  
26992     },
26993      
26994     
26995     /**
26996      * Sets the value of the item. 
26997      * @param {String} either an object  or a string..
26998      */
26999     setValue : function(v){
27000         //this.value = v;
27001         v = v || []; // empty set..
27002         // this does not seem smart - it really only affects memoryproxy grids..
27003         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
27004             var ds = this.grid.getDataSource();
27005             // assumes a json reader..
27006             var data = {}
27007             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
27008             ds.loadData( data);
27009         }
27010         // clear selection so it does not get stale.
27011         if (this.grid.sm) { 
27012             this.grid.sm.clearSelections();
27013         }
27014         
27015         Roo.form.GridField.superclass.setValue.call(this, v);
27016         this.refreshValue();
27017         // should load data in the grid really....
27018     },
27019     
27020     // private
27021     refreshValue: function() {
27022          var val = [];
27023         this.grid.getDataSource().each(function(r) {
27024             val.push(r.data);
27025         });
27026         this.el.dom.value = Roo.encode(val);
27027     }
27028     
27029      
27030     
27031     
27032 });/*
27033  * Based on:
27034  * Ext JS Library 1.1.1
27035  * Copyright(c) 2006-2007, Ext JS, LLC.
27036  *
27037  * Originally Released Under LGPL - original licence link has changed is not relivant.
27038  *
27039  * Fork - LGPL
27040  * <script type="text/javascript">
27041  */
27042 /**
27043  * @class Roo.form.DisplayField
27044  * @extends Roo.form.Field
27045  * A generic Field to display non-editable data.
27046  * @cfg {Boolean} closable (true|false) default false
27047  * @constructor
27048  * Creates a new Display Field item.
27049  * @param {Object} config Configuration options
27050  */
27051 Roo.form.DisplayField = function(config){
27052     Roo.form.DisplayField.superclass.constructor.call(this, config);
27053     
27054     this.addEvents({
27055         /**
27056          * @event close
27057          * Fires after the click the close btn
27058              * @param {Roo.form.DisplayField} this
27059              */
27060         close : true
27061     });
27062 };
27063
27064 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
27065     inputType:      'hidden',
27066     allowBlank:     true,
27067     readOnly:         true,
27068     
27069  
27070     /**
27071      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27072      */
27073     focusClass : undefined,
27074     /**
27075      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27076      */
27077     fieldClass: 'x-form-field',
27078     
27079      /**
27080      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
27081      */
27082     valueRenderer: undefined,
27083     
27084     width: 100,
27085     /**
27086      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27087      * {tag: "input", type: "checkbox", autocomplete: "off"})
27088      */
27089      
27090  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27091  
27092     closable : false,
27093     
27094     onResize : function(){
27095         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
27096         
27097     },
27098
27099     initEvents : function(){
27100         // Roo.form.Checkbox.superclass.initEvents.call(this);
27101         // has no events...
27102         
27103         if(this.closable){
27104             this.closeEl.on('click', this.onClose, this);
27105         }
27106        
27107     },
27108
27109
27110     getResizeEl : function(){
27111         return this.wrap;
27112     },
27113
27114     getPositionEl : function(){
27115         return this.wrap;
27116     },
27117
27118     // private
27119     onRender : function(ct, position){
27120         
27121         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
27122         //if(this.inputValue !== undefined){
27123         this.wrap = this.el.wrap();
27124         
27125         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
27126         
27127         if(this.closable){
27128             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
27129         }
27130         
27131         if (this.bodyStyle) {
27132             this.viewEl.applyStyles(this.bodyStyle);
27133         }
27134         //this.viewEl.setStyle('padding', '2px');
27135         
27136         this.setValue(this.value);
27137         
27138     },
27139 /*
27140     // private
27141     initValue : Roo.emptyFn,
27142
27143   */
27144
27145         // private
27146     onClick : function(){
27147         
27148     },
27149
27150     /**
27151      * Sets the checked state of the checkbox.
27152      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
27153      */
27154     setValue : function(v){
27155         this.value = v;
27156         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
27157         // this might be called before we have a dom element..
27158         if (!this.viewEl) {
27159             return;
27160         }
27161         this.viewEl.dom.innerHTML = html;
27162         Roo.form.DisplayField.superclass.setValue.call(this, v);
27163
27164     },
27165     
27166     onClose : function(e)
27167     {
27168         e.preventDefault();
27169         
27170         this.fireEvent('close', this);
27171     }
27172 });/*
27173  * 
27174  * Licence- LGPL
27175  * 
27176  */
27177
27178 /**
27179  * @class Roo.form.DayPicker
27180  * @extends Roo.form.Field
27181  * A Day picker show [M] [T] [W] ....
27182  * @constructor
27183  * Creates a new Day Picker
27184  * @param {Object} config Configuration options
27185  */
27186 Roo.form.DayPicker= function(config){
27187     Roo.form.DayPicker.superclass.constructor.call(this, config);
27188      
27189 };
27190
27191 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
27192     /**
27193      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27194      */
27195     focusClass : undefined,
27196     /**
27197      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27198      */
27199     fieldClass: "x-form-field",
27200    
27201     /**
27202      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27203      * {tag: "input", type: "checkbox", autocomplete: "off"})
27204      */
27205     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
27206     
27207    
27208     actionMode : 'viewEl', 
27209     //
27210     // private
27211  
27212     inputType : 'hidden',
27213     
27214      
27215     inputElement: false, // real input element?
27216     basedOn: false, // ????
27217     
27218     isFormField: true, // not sure where this is needed!!!!
27219
27220     onResize : function(){
27221         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
27222         if(!this.boxLabel){
27223             this.el.alignTo(this.wrap, 'c-c');
27224         }
27225     },
27226
27227     initEvents : function(){
27228         Roo.form.Checkbox.superclass.initEvents.call(this);
27229         this.el.on("click", this.onClick,  this);
27230         this.el.on("change", this.onClick,  this);
27231     },
27232
27233
27234     getResizeEl : function(){
27235         return this.wrap;
27236     },
27237
27238     getPositionEl : function(){
27239         return this.wrap;
27240     },
27241
27242     
27243     // private
27244     onRender : function(ct, position){
27245         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
27246        
27247         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
27248         
27249         var r1 = '<table><tr>';
27250         var r2 = '<tr class="x-form-daypick-icons">';
27251         for (var i=0; i < 7; i++) {
27252             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
27253             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
27254         }
27255         
27256         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
27257         viewEl.select('img').on('click', this.onClick, this);
27258         this.viewEl = viewEl;   
27259         
27260         
27261         // this will not work on Chrome!!!
27262         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
27263         this.el.on('propertychange', this.setFromHidden,  this);  //ie
27264         
27265         
27266           
27267
27268     },
27269
27270     // private
27271     initValue : Roo.emptyFn,
27272
27273     /**
27274      * Returns the checked state of the checkbox.
27275      * @return {Boolean} True if checked, else false
27276      */
27277     getValue : function(){
27278         return this.el.dom.value;
27279         
27280     },
27281
27282         // private
27283     onClick : function(e){ 
27284         //this.setChecked(!this.checked);
27285         Roo.get(e.target).toggleClass('x-menu-item-checked');
27286         this.refreshValue();
27287         //if(this.el.dom.checked != this.checked){
27288         //    this.setValue(this.el.dom.checked);
27289        // }
27290     },
27291     
27292     // private
27293     refreshValue : function()
27294     {
27295         var val = '';
27296         this.viewEl.select('img',true).each(function(e,i,n)  {
27297             val += e.is(".x-menu-item-checked") ? String(n) : '';
27298         });
27299         this.setValue(val, true);
27300     },
27301
27302     /**
27303      * Sets the checked state of the checkbox.
27304      * On is always based on a string comparison between inputValue and the param.
27305      * @param {Boolean/String} value - the value to set 
27306      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
27307      */
27308     setValue : function(v,suppressEvent){
27309         if (!this.el.dom) {
27310             return;
27311         }
27312         var old = this.el.dom.value ;
27313         this.el.dom.value = v;
27314         if (suppressEvent) {
27315             return ;
27316         }
27317          
27318         // update display..
27319         this.viewEl.select('img',true).each(function(e,i,n)  {
27320             
27321             var on = e.is(".x-menu-item-checked");
27322             var newv = v.indexOf(String(n)) > -1;
27323             if (on != newv) {
27324                 e.toggleClass('x-menu-item-checked');
27325             }
27326             
27327         });
27328         
27329         
27330         this.fireEvent('change', this, v, old);
27331         
27332         
27333     },
27334    
27335     // handle setting of hidden value by some other method!!?!?
27336     setFromHidden: function()
27337     {
27338         if(!this.el){
27339             return;
27340         }
27341         //console.log("SET FROM HIDDEN");
27342         //alert('setFrom hidden');
27343         this.setValue(this.el.dom.value);
27344     },
27345     
27346     onDestroy : function()
27347     {
27348         if(this.viewEl){
27349             Roo.get(this.viewEl).remove();
27350         }
27351          
27352         Roo.form.DayPicker.superclass.onDestroy.call(this);
27353     }
27354
27355 });/*
27356  * RooJS Library 1.1.1
27357  * Copyright(c) 2008-2011  Alan Knowles
27358  *
27359  * License - LGPL
27360  */
27361  
27362
27363 /**
27364  * @class Roo.form.ComboCheck
27365  * @extends Roo.form.ComboBox
27366  * A combobox for multiple select items.
27367  *
27368  * FIXME - could do with a reset button..
27369  * 
27370  * @constructor
27371  * Create a new ComboCheck
27372  * @param {Object} config Configuration options
27373  */
27374 Roo.form.ComboCheck = function(config){
27375     Roo.form.ComboCheck.superclass.constructor.call(this, config);
27376     // should verify some data...
27377     // like
27378     // hiddenName = required..
27379     // displayField = required
27380     // valudField == required
27381     var req= [ 'hiddenName', 'displayField', 'valueField' ];
27382     var _t = this;
27383     Roo.each(req, function(e) {
27384         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
27385             throw "Roo.form.ComboCheck : missing value for: " + e;
27386         }
27387     });
27388     
27389     
27390 };
27391
27392 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
27393      
27394      
27395     editable : false,
27396      
27397     selectedClass: 'x-menu-item-checked', 
27398     
27399     // private
27400     onRender : function(ct, position){
27401         var _t = this;
27402         
27403         
27404         
27405         if(!this.tpl){
27406             var cls = 'x-combo-list';
27407
27408             
27409             this.tpl =  new Roo.Template({
27410                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
27411                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
27412                    '<span>{' + this.displayField + '}</span>' +
27413                     '</div>' 
27414                 
27415             });
27416         }
27417  
27418         
27419         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
27420         this.view.singleSelect = false;
27421         this.view.multiSelect = true;
27422         this.view.toggleSelect = true;
27423         this.pageTb.add(new Roo.Toolbar.Fill(), {
27424             
27425             text: 'Done',
27426             handler: function()
27427             {
27428                 _t.collapse();
27429             }
27430         });
27431     },
27432     
27433     onViewOver : function(e, t){
27434         // do nothing...
27435         return;
27436         
27437     },
27438     
27439     onViewClick : function(doFocus,index){
27440         return;
27441         
27442     },
27443     select: function () {
27444         //Roo.log("SELECT CALLED");
27445     },
27446      
27447     selectByValue : function(xv, scrollIntoView){
27448         var ar = this.getValueArray();
27449         var sels = [];
27450         
27451         Roo.each(ar, function(v) {
27452             if(v === undefined || v === null){
27453                 return;
27454             }
27455             var r = this.findRecord(this.valueField, v);
27456             if(r){
27457                 sels.push(this.store.indexOf(r))
27458                 
27459             }
27460         },this);
27461         this.view.select(sels);
27462         return false;
27463     },
27464     
27465     
27466     
27467     onSelect : function(record, index){
27468        // Roo.log("onselect Called");
27469        // this is only called by the clear button now..
27470         this.view.clearSelections();
27471         this.setValue('[]');
27472         if (this.value != this.valueBefore) {
27473             this.fireEvent('change', this, this.value, this.valueBefore);
27474             this.valueBefore = this.value;
27475         }
27476     },
27477     getValueArray : function()
27478     {
27479         var ar = [] ;
27480         
27481         try {
27482             //Roo.log(this.value);
27483             if (typeof(this.value) == 'undefined') {
27484                 return [];
27485             }
27486             var ar = Roo.decode(this.value);
27487             return  ar instanceof Array ? ar : []; //?? valid?
27488             
27489         } catch(e) {
27490             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
27491             return [];
27492         }
27493          
27494     },
27495     expand : function ()
27496     {
27497         
27498         Roo.form.ComboCheck.superclass.expand.call(this);
27499         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27500         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27501         
27502
27503     },
27504     
27505     collapse : function(){
27506         Roo.form.ComboCheck.superclass.collapse.call(this);
27507         var sl = this.view.getSelectedIndexes();
27508         var st = this.store;
27509         var nv = [];
27510         var tv = [];
27511         var r;
27512         Roo.each(sl, function(i) {
27513             r = st.getAt(i);
27514             nv.push(r.get(this.valueField));
27515         },this);
27516         this.setValue(Roo.encode(nv));
27517         if (this.value != this.valueBefore) {
27518
27519             this.fireEvent('change', this, this.value, this.valueBefore);
27520             this.valueBefore = this.value;
27521         }
27522         
27523     },
27524     
27525     setValue : function(v){
27526         // Roo.log(v);
27527         this.value = v;
27528         
27529         var vals = this.getValueArray();
27530         var tv = [];
27531         Roo.each(vals, function(k) {
27532             var r = this.findRecord(this.valueField, k);
27533             if(r){
27534                 tv.push(r.data[this.displayField]);
27535             }else if(this.valueNotFoundText !== undefined){
27536                 tv.push( this.valueNotFoundText );
27537             }
27538         },this);
27539        // Roo.log(tv);
27540         
27541         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27542         this.hiddenField.value = v;
27543         this.value = v;
27544     }
27545     
27546 });/*
27547  * Based on:
27548  * Ext JS Library 1.1.1
27549  * Copyright(c) 2006-2007, Ext JS, LLC.
27550  *
27551  * Originally Released Under LGPL - original licence link has changed is not relivant.
27552  *
27553  * Fork - LGPL
27554  * <script type="text/javascript">
27555  */
27556  
27557 /**
27558  * @class Roo.form.Signature
27559  * @extends Roo.form.Field
27560  * Signature field.  
27561  * @constructor
27562  * 
27563  * @param {Object} config Configuration options
27564  */
27565
27566 Roo.form.Signature = function(config){
27567     Roo.form.Signature.superclass.constructor.call(this, config);
27568     
27569     this.addEvents({// not in used??
27570          /**
27571          * @event confirm
27572          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27573              * @param {Roo.form.Signature} combo This combo box
27574              */
27575         'confirm' : true,
27576         /**
27577          * @event reset
27578          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27579              * @param {Roo.form.ComboBox} combo This combo box
27580              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27581              */
27582         'reset' : true
27583     });
27584 };
27585
27586 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
27587     /**
27588      * @cfg {Object} labels Label to use when rendering a form.
27589      * defaults to 
27590      * labels : { 
27591      *      clear : "Clear",
27592      *      confirm : "Confirm"
27593      *  }
27594      */
27595     labels : { 
27596         clear : "Clear",
27597         confirm : "Confirm"
27598     },
27599     /**
27600      * @cfg {Number} width The signature panel width (defaults to 300)
27601      */
27602     width: 300,
27603     /**
27604      * @cfg {Number} height The signature panel height (defaults to 100)
27605      */
27606     height : 100,
27607     /**
27608      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27609      */
27610     allowBlank : false,
27611     
27612     //private
27613     // {Object} signPanel The signature SVG panel element (defaults to {})
27614     signPanel : {},
27615     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27616     isMouseDown : false,
27617     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27618     isConfirmed : false,
27619     // {String} signatureTmp SVG mapping string (defaults to empty string)
27620     signatureTmp : '',
27621     
27622     
27623     defaultAutoCreate : { // modified by initCompnoent..
27624         tag: "input",
27625         type:"hidden"
27626     },
27627
27628     // private
27629     onRender : function(ct, position){
27630         
27631         Roo.form.Signature.superclass.onRender.call(this, ct, position);
27632         
27633         this.wrap = this.el.wrap({
27634             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27635         });
27636         
27637         this.createToolbar(this);
27638         this.signPanel = this.wrap.createChild({
27639                 tag: 'div',
27640                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27641             }, this.el
27642         );
27643             
27644         this.svgID = Roo.id();
27645         this.svgEl = this.signPanel.createChild({
27646               xmlns : 'http://www.w3.org/2000/svg',
27647               tag : 'svg',
27648               id : this.svgID + "-svg",
27649               width: this.width,
27650               height: this.height,
27651               viewBox: '0 0 '+this.width+' '+this.height,
27652               cn : [
27653                 {
27654                     tag: "rect",
27655                     id: this.svgID + "-svg-r",
27656                     width: this.width,
27657                     height: this.height,
27658                     fill: "#ffa"
27659                 },
27660                 {
27661                     tag: "line",
27662                     id: this.svgID + "-svg-l",
27663                     x1: "0", // start
27664                     y1: (this.height*0.8), // start set the line in 80% of height
27665                     x2: this.width, // end
27666                     y2: (this.height*0.8), // end set the line in 80% of height
27667                     'stroke': "#666",
27668                     'stroke-width': "1",
27669                     'stroke-dasharray': "3",
27670                     'shape-rendering': "crispEdges",
27671                     'pointer-events': "none"
27672                 },
27673                 {
27674                     tag: "path",
27675                     id: this.svgID + "-svg-p",
27676                     'stroke': "navy",
27677                     'stroke-width': "3",
27678                     'fill': "none",
27679                     'pointer-events': 'none'
27680                 }
27681               ]
27682         });
27683         this.createSVG();
27684         this.svgBox = this.svgEl.dom.getScreenCTM();
27685     },
27686     createSVG : function(){ 
27687         var svg = this.signPanel;
27688         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27689         var t = this;
27690
27691         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27692         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27693         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27694         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27695         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27696         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27697         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27698         
27699     },
27700     isTouchEvent : function(e){
27701         return e.type.match(/^touch/);
27702     },
27703     getCoords : function (e) {
27704         var pt    = this.svgEl.dom.createSVGPoint();
27705         pt.x = e.clientX; 
27706         pt.y = e.clientY;
27707         if (this.isTouchEvent(e)) {
27708             pt.x =  e.targetTouches[0].clientX;
27709             pt.y = e.targetTouches[0].clientY;
27710         }
27711         var a = this.svgEl.dom.getScreenCTM();
27712         var b = a.inverse();
27713         var mx = pt.matrixTransform(b);
27714         return mx.x + ',' + mx.y;
27715     },
27716     //mouse event headler 
27717     down : function (e) {
27718         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27719         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27720         
27721         this.isMouseDown = true;
27722         
27723         e.preventDefault();
27724     },
27725     move : function (e) {
27726         if (this.isMouseDown) {
27727             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27728             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27729         }
27730         
27731         e.preventDefault();
27732     },
27733     up : function (e) {
27734         this.isMouseDown = false;
27735         var sp = this.signatureTmp.split(' ');
27736         
27737         if(sp.length > 1){
27738             if(!sp[sp.length-2].match(/^L/)){
27739                 sp.pop();
27740                 sp.pop();
27741                 sp.push("");
27742                 this.signatureTmp = sp.join(" ");
27743             }
27744         }
27745         if(this.getValue() != this.signatureTmp){
27746             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27747             this.isConfirmed = false;
27748         }
27749         e.preventDefault();
27750     },
27751     
27752     /**
27753      * Protected method that will not generally be called directly. It
27754      * is called when the editor creates its toolbar. Override this method if you need to
27755      * add custom toolbar buttons.
27756      * @param {HtmlEditor} editor
27757      */
27758     createToolbar : function(editor){
27759          function btn(id, toggle, handler){
27760             var xid = fid + '-'+ id ;
27761             return {
27762                 id : xid,
27763                 cmd : id,
27764                 cls : 'x-btn-icon x-edit-'+id,
27765                 enableToggle:toggle !== false,
27766                 scope: editor, // was editor...
27767                 handler:handler||editor.relayBtnCmd,
27768                 clickEvent:'mousedown',
27769                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27770                 tabIndex:-1
27771             };
27772         }
27773         
27774         
27775         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27776         this.tb = tb;
27777         this.tb.add(
27778            {
27779                 cls : ' x-signature-btn x-signature-'+id,
27780                 scope: editor, // was editor...
27781                 handler: this.reset,
27782                 clickEvent:'mousedown',
27783                 text: this.labels.clear
27784             },
27785             {
27786                  xtype : 'Fill',
27787                  xns: Roo.Toolbar
27788             }, 
27789             {
27790                 cls : '  x-signature-btn x-signature-'+id,
27791                 scope: editor, // was editor...
27792                 handler: this.confirmHandler,
27793                 clickEvent:'mousedown',
27794                 text: this.labels.confirm
27795             }
27796         );
27797     
27798     },
27799     //public
27800     /**
27801      * when user is clicked confirm then show this image.....
27802      * 
27803      * @return {String} Image Data URI
27804      */
27805     getImageDataURI : function(){
27806         var svg = this.svgEl.dom.parentNode.innerHTML;
27807         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
27808         return src; 
27809     },
27810     /**
27811      * 
27812      * @return {Boolean} this.isConfirmed
27813      */
27814     getConfirmed : function(){
27815         return this.isConfirmed;
27816     },
27817     /**
27818      * 
27819      * @return {Number} this.width
27820      */
27821     getWidth : function(){
27822         return this.width;
27823     },
27824     /**
27825      * 
27826      * @return {Number} this.height
27827      */
27828     getHeight : function(){
27829         return this.height;
27830     },
27831     // private
27832     getSignature : function(){
27833         return this.signatureTmp;
27834     },
27835     // private
27836     reset : function(){
27837         this.signatureTmp = '';
27838         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27839         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
27840         this.isConfirmed = false;
27841         Roo.form.Signature.superclass.reset.call(this);
27842     },
27843     setSignature : function(s){
27844         this.signatureTmp = s;
27845         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27846         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
27847         this.setValue(s);
27848         this.isConfirmed = false;
27849         Roo.form.Signature.superclass.reset.call(this);
27850     }, 
27851     test : function(){
27852 //        Roo.log(this.signPanel.dom.contentWindow.up())
27853     },
27854     //private
27855     setConfirmed : function(){
27856         
27857         
27858         
27859 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
27860     },
27861     // private
27862     confirmHandler : function(){
27863         if(!this.getSignature()){
27864             return;
27865         }
27866         
27867         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
27868         this.setValue(this.getSignature());
27869         this.isConfirmed = true;
27870         
27871         this.fireEvent('confirm', this);
27872     },
27873     // private
27874     // Subclasses should provide the validation implementation by overriding this
27875     validateValue : function(value){
27876         if(this.allowBlank){
27877             return true;
27878         }
27879         
27880         if(this.isConfirmed){
27881             return true;
27882         }
27883         return false;
27884     }
27885 });/*
27886  * Based on:
27887  * Ext JS Library 1.1.1
27888  * Copyright(c) 2006-2007, Ext JS, LLC.
27889  *
27890  * Originally Released Under LGPL - original licence link has changed is not relivant.
27891  *
27892  * Fork - LGPL
27893  * <script type="text/javascript">
27894  */
27895  
27896
27897 /**
27898  * @class Roo.form.ComboBox
27899  * @extends Roo.form.TriggerField
27900  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
27901  * @constructor
27902  * Create a new ComboBox.
27903  * @param {Object} config Configuration options
27904  */
27905 Roo.form.Select = function(config){
27906     Roo.form.Select.superclass.constructor.call(this, config);
27907      
27908 };
27909
27910 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
27911     /**
27912      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
27913      */
27914     /**
27915      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
27916      * rendering into an Roo.Editor, defaults to false)
27917      */
27918     /**
27919      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
27920      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
27921      */
27922     /**
27923      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
27924      */
27925     /**
27926      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
27927      * the dropdown list (defaults to undefined, with no header element)
27928      */
27929
27930      /**
27931      * @cfg {String/Roo.Template} tpl The template to use to render the output
27932      */
27933      
27934     // private
27935     defaultAutoCreate : {tag: "select"  },
27936     /**
27937      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
27938      */
27939     listWidth: undefined,
27940     /**
27941      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
27942      * mode = 'remote' or 'text' if mode = 'local')
27943      */
27944     displayField: undefined,
27945     /**
27946      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
27947      * mode = 'remote' or 'value' if mode = 'local'). 
27948      * Note: use of a valueField requires the user make a selection
27949      * in order for a value to be mapped.
27950      */
27951     valueField: undefined,
27952     
27953     
27954     /**
27955      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
27956      * field's data value (defaults to the underlying DOM element's name)
27957      */
27958     hiddenName: undefined,
27959     /**
27960      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
27961      */
27962     listClass: '',
27963     /**
27964      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
27965      */
27966     selectedClass: 'x-combo-selected',
27967     /**
27968      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
27969      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
27970      * which displays a downward arrow icon).
27971      */
27972     triggerClass : 'x-form-arrow-trigger',
27973     /**
27974      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
27975      */
27976     shadow:'sides',
27977     /**
27978      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
27979      * anchor positions (defaults to 'tl-bl')
27980      */
27981     listAlign: 'tl-bl?',
27982     /**
27983      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
27984      */
27985     maxHeight: 300,
27986     /**
27987      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
27988      * query specified by the allQuery config option (defaults to 'query')
27989      */
27990     triggerAction: 'query',
27991     /**
27992      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
27993      * (defaults to 4, does not apply if editable = false)
27994      */
27995     minChars : 4,
27996     /**
27997      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
27998      * delay (typeAheadDelay) if it matches a known value (defaults to false)
27999      */
28000     typeAhead: false,
28001     /**
28002      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
28003      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
28004      */
28005     queryDelay: 500,
28006     /**
28007      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
28008      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
28009      */
28010     pageSize: 0,
28011     /**
28012      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
28013      * when editable = true (defaults to false)
28014      */
28015     selectOnFocus:false,
28016     /**
28017      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
28018      */
28019     queryParam: 'query',
28020     /**
28021      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
28022      * when mode = 'remote' (defaults to 'Loading...')
28023      */
28024     loadingText: 'Loading...',
28025     /**
28026      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
28027      */
28028     resizable: false,
28029     /**
28030      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
28031      */
28032     handleHeight : 8,
28033     /**
28034      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
28035      * traditional select (defaults to true)
28036      */
28037     editable: true,
28038     /**
28039      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
28040      */
28041     allQuery: '',
28042     /**
28043      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
28044      */
28045     mode: 'remote',
28046     /**
28047      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
28048      * listWidth has a higher value)
28049      */
28050     minListWidth : 70,
28051     /**
28052      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
28053      * allow the user to set arbitrary text into the field (defaults to false)
28054      */
28055     forceSelection:false,
28056     /**
28057      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
28058      * if typeAhead = true (defaults to 250)
28059      */
28060     typeAheadDelay : 250,
28061     /**
28062      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
28063      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
28064      */
28065     valueNotFoundText : undefined,
28066     
28067     /**
28068      * @cfg {String} defaultValue The value displayed after loading the store.
28069      */
28070     defaultValue: '',
28071     
28072     /**
28073      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
28074      */
28075     blockFocus : false,
28076     
28077     /**
28078      * @cfg {Boolean} disableClear Disable showing of clear button.
28079      */
28080     disableClear : false,
28081     /**
28082      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
28083      */
28084     alwaysQuery : false,
28085     
28086     //private
28087     addicon : false,
28088     editicon: false,
28089     
28090     // element that contains real text value.. (when hidden is used..)
28091      
28092     // private
28093     onRender : function(ct, position){
28094         Roo.form.Field.prototype.onRender.call(this, ct, position);
28095         
28096         if(this.store){
28097             this.store.on('beforeload', this.onBeforeLoad, this);
28098             this.store.on('load', this.onLoad, this);
28099             this.store.on('loadexception', this.onLoadException, this);
28100             this.store.load({});
28101         }
28102         
28103         
28104         
28105     },
28106
28107     // private
28108     initEvents : function(){
28109         //Roo.form.ComboBox.superclass.initEvents.call(this);
28110  
28111     },
28112
28113     onDestroy : function(){
28114        
28115         if(this.store){
28116             this.store.un('beforeload', this.onBeforeLoad, this);
28117             this.store.un('load', this.onLoad, this);
28118             this.store.un('loadexception', this.onLoadException, this);
28119         }
28120         //Roo.form.ComboBox.superclass.onDestroy.call(this);
28121     },
28122
28123     // private
28124     fireKey : function(e){
28125         if(e.isNavKeyPress() && !this.list.isVisible()){
28126             this.fireEvent("specialkey", this, e);
28127         }
28128     },
28129
28130     // private
28131     onResize: function(w, h){
28132         
28133         return; 
28134     
28135         
28136     },
28137
28138     /**
28139      * Allow or prevent the user from directly editing the field text.  If false is passed,
28140      * the user will only be able to select from the items defined in the dropdown list.  This method
28141      * is the runtime equivalent of setting the 'editable' config option at config time.
28142      * @param {Boolean} value True to allow the user to directly edit the field text
28143      */
28144     setEditable : function(value){
28145          
28146     },
28147
28148     // private
28149     onBeforeLoad : function(){
28150         
28151         Roo.log("Select before load");
28152         return;
28153     
28154         this.innerList.update(this.loadingText ?
28155                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
28156         //this.restrictHeight();
28157         this.selectedIndex = -1;
28158     },
28159
28160     // private
28161     onLoad : function(){
28162
28163     
28164         var dom = this.el.dom;
28165         dom.innerHTML = '';
28166          var od = dom.ownerDocument;
28167          
28168         if (this.emptyText) {
28169             var op = od.createElement('option');
28170             op.setAttribute('value', '');
28171             op.innerHTML = String.format('{0}', this.emptyText);
28172             dom.appendChild(op);
28173         }
28174         if(this.store.getCount() > 0){
28175            
28176             var vf = this.valueField;
28177             var df = this.displayField;
28178             this.store.data.each(function(r) {
28179                 // which colmsn to use... testing - cdoe / title..
28180                 var op = od.createElement('option');
28181                 op.setAttribute('value', r.data[vf]);
28182                 op.innerHTML = String.format('{0}', r.data[df]);
28183                 dom.appendChild(op);
28184             });
28185             if (typeof(this.defaultValue != 'undefined')) {
28186                 this.setValue(this.defaultValue);
28187             }
28188             
28189              
28190         }else{
28191             //this.onEmptyResults();
28192         }
28193         //this.el.focus();
28194     },
28195     // private
28196     onLoadException : function()
28197     {
28198         dom.innerHTML = '';
28199             
28200         Roo.log("Select on load exception");
28201         return;
28202     
28203         this.collapse();
28204         Roo.log(this.store.reader.jsonData);
28205         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
28206             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
28207         }
28208         
28209         
28210     },
28211     // private
28212     onTypeAhead : function(){
28213          
28214     },
28215
28216     // private
28217     onSelect : function(record, index){
28218         Roo.log('on select?');
28219         return;
28220         if(this.fireEvent('beforeselect', this, record, index) !== false){
28221             this.setFromData(index > -1 ? record.data : false);
28222             this.collapse();
28223             this.fireEvent('select', this, record, index);
28224         }
28225     },
28226
28227     /**
28228      * Returns the currently selected field value or empty string if no value is set.
28229      * @return {String} value The selected value
28230      */
28231     getValue : function(){
28232         var dom = this.el.dom;
28233         this.value = dom.options[dom.selectedIndex].value;
28234         return this.value;
28235         
28236     },
28237
28238     /**
28239      * Clears any text/value currently set in the field
28240      */
28241     clearValue : function(){
28242         this.value = '';
28243         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
28244         
28245     },
28246
28247     /**
28248      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
28249      * will be displayed in the field.  If the value does not match the data value of an existing item,
28250      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
28251      * Otherwise the field will be blank (although the value will still be set).
28252      * @param {String} value The value to match
28253      */
28254     setValue : function(v){
28255         var d = this.el.dom;
28256         for (var i =0; i < d.options.length;i++) {
28257             if (v == d.options[i].value) {
28258                 d.selectedIndex = i;
28259                 this.value = v;
28260                 return;
28261             }
28262         }
28263         this.clearValue();
28264     },
28265     /**
28266      * @property {Object} the last set data for the element
28267      */
28268     
28269     lastData : false,
28270     /**
28271      * Sets the value of the field based on a object which is related to the record format for the store.
28272      * @param {Object} value the value to set as. or false on reset?
28273      */
28274     setFromData : function(o){
28275         Roo.log('setfrom data?');
28276          
28277         
28278         
28279     },
28280     // private
28281     reset : function(){
28282         this.clearValue();
28283     },
28284     // private
28285     findRecord : function(prop, value){
28286         
28287         return false;
28288     
28289         var record;
28290         if(this.store.getCount() > 0){
28291             this.store.each(function(r){
28292                 if(r.data[prop] == value){
28293                     record = r;
28294                     return false;
28295                 }
28296                 return true;
28297             });
28298         }
28299         return record;
28300     },
28301     
28302     getName: function()
28303     {
28304         // returns hidden if it's set..
28305         if (!this.rendered) {return ''};
28306         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
28307         
28308     },
28309      
28310
28311     
28312
28313     // private
28314     onEmptyResults : function(){
28315         Roo.log('empty results');
28316         //this.collapse();
28317     },
28318
28319     /**
28320      * Returns true if the dropdown list is expanded, else false.
28321      */
28322     isExpanded : function(){
28323         return false;
28324     },
28325
28326     /**
28327      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
28328      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28329      * @param {String} value The data value of the item to select
28330      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28331      * selected item if it is not currently in view (defaults to true)
28332      * @return {Boolean} True if the value matched an item in the list, else false
28333      */
28334     selectByValue : function(v, scrollIntoView){
28335         Roo.log('select By Value');
28336         return false;
28337     
28338         if(v !== undefined && v !== null){
28339             var r = this.findRecord(this.valueField || this.displayField, v);
28340             if(r){
28341                 this.select(this.store.indexOf(r), scrollIntoView);
28342                 return true;
28343             }
28344         }
28345         return false;
28346     },
28347
28348     /**
28349      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
28350      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28351      * @param {Number} index The zero-based index of the list item to select
28352      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28353      * selected item if it is not currently in view (defaults to true)
28354      */
28355     select : function(index, scrollIntoView){
28356         Roo.log('select ');
28357         return  ;
28358         
28359         this.selectedIndex = index;
28360         this.view.select(index);
28361         if(scrollIntoView !== false){
28362             var el = this.view.getNode(index);
28363             if(el){
28364                 this.innerList.scrollChildIntoView(el, false);
28365             }
28366         }
28367     },
28368
28369       
28370
28371     // private
28372     validateBlur : function(){
28373         
28374         return;
28375         
28376     },
28377
28378     // private
28379     initQuery : function(){
28380         this.doQuery(this.getRawValue());
28381     },
28382
28383     // private
28384     doForce : function(){
28385         if(this.el.dom.value.length > 0){
28386             this.el.dom.value =
28387                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
28388              
28389         }
28390     },
28391
28392     /**
28393      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
28394      * query allowing the query action to be canceled if needed.
28395      * @param {String} query The SQL query to execute
28396      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
28397      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
28398      * saved in the current store (defaults to false)
28399      */
28400     doQuery : function(q, forceAll){
28401         
28402         Roo.log('doQuery?');
28403         if(q === undefined || q === null){
28404             q = '';
28405         }
28406         var qe = {
28407             query: q,
28408             forceAll: forceAll,
28409             combo: this,
28410             cancel:false
28411         };
28412         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
28413             return false;
28414         }
28415         q = qe.query;
28416         forceAll = qe.forceAll;
28417         if(forceAll === true || (q.length >= this.minChars)){
28418             if(this.lastQuery != q || this.alwaysQuery){
28419                 this.lastQuery = q;
28420                 if(this.mode == 'local'){
28421                     this.selectedIndex = -1;
28422                     if(forceAll){
28423                         this.store.clearFilter();
28424                     }else{
28425                         this.store.filter(this.displayField, q);
28426                     }
28427                     this.onLoad();
28428                 }else{
28429                     this.store.baseParams[this.queryParam] = q;
28430                     this.store.load({
28431                         params: this.getParams(q)
28432                     });
28433                     this.expand();
28434                 }
28435             }else{
28436                 this.selectedIndex = -1;
28437                 this.onLoad();   
28438             }
28439         }
28440     },
28441
28442     // private
28443     getParams : function(q){
28444         var p = {};
28445         //p[this.queryParam] = q;
28446         if(this.pageSize){
28447             p.start = 0;
28448             p.limit = this.pageSize;
28449         }
28450         return p;
28451     },
28452
28453     /**
28454      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
28455      */
28456     collapse : function(){
28457         
28458     },
28459
28460     // private
28461     collapseIf : function(e){
28462         
28463     },
28464
28465     /**
28466      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28467      */
28468     expand : function(){
28469         
28470     } ,
28471
28472     // private
28473      
28474
28475     /** 
28476     * @cfg {Boolean} grow 
28477     * @hide 
28478     */
28479     /** 
28480     * @cfg {Number} growMin 
28481     * @hide 
28482     */
28483     /** 
28484     * @cfg {Number} growMax 
28485     * @hide 
28486     */
28487     /**
28488      * @hide
28489      * @method autoSize
28490      */
28491     
28492     setWidth : function()
28493     {
28494         
28495     },
28496     getResizeEl : function(){
28497         return this.el;
28498     }
28499 });//<script type="text/javasscript">
28500  
28501
28502 /**
28503  * @class Roo.DDView
28504  * A DnD enabled version of Roo.View.
28505  * @param {Element/String} container The Element in which to create the View.
28506  * @param {String} tpl The template string used to create the markup for each element of the View
28507  * @param {Object} config The configuration properties. These include all the config options of
28508  * {@link Roo.View} plus some specific to this class.<br>
28509  * <p>
28510  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28511  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28512  * <p>
28513  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28514 .x-view-drag-insert-above {
28515         border-top:1px dotted #3366cc;
28516 }
28517 .x-view-drag-insert-below {
28518         border-bottom:1px dotted #3366cc;
28519 }
28520 </code></pre>
28521  * 
28522  */
28523  
28524 Roo.DDView = function(container, tpl, config) {
28525     Roo.DDView.superclass.constructor.apply(this, arguments);
28526     this.getEl().setStyle("outline", "0px none");
28527     this.getEl().unselectable();
28528     if (this.dragGroup) {
28529         this.setDraggable(this.dragGroup.split(","));
28530     }
28531     if (this.dropGroup) {
28532         this.setDroppable(this.dropGroup.split(","));
28533     }
28534     if (this.deletable) {
28535         this.setDeletable();
28536     }
28537     this.isDirtyFlag = false;
28538         this.addEvents({
28539                 "drop" : true
28540         });
28541 };
28542
28543 Roo.extend(Roo.DDView, Roo.View, {
28544 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28545 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28546 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28547 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28548
28549         isFormField: true,
28550
28551         reset: Roo.emptyFn,
28552         
28553         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28554
28555         validate: function() {
28556                 return true;
28557         },
28558         
28559         destroy: function() {
28560                 this.purgeListeners();
28561                 this.getEl.removeAllListeners();
28562                 this.getEl().remove();
28563                 if (this.dragZone) {
28564                         if (this.dragZone.destroy) {
28565                                 this.dragZone.destroy();
28566                         }
28567                 }
28568                 if (this.dropZone) {
28569                         if (this.dropZone.destroy) {
28570                                 this.dropZone.destroy();
28571                         }
28572                 }
28573         },
28574
28575 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28576         getName: function() {
28577                 return this.name;
28578         },
28579
28580 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28581         setValue: function(v) {
28582                 if (!this.store) {
28583                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28584                 }
28585                 var data = {};
28586                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28587                 this.store.proxy = new Roo.data.MemoryProxy(data);
28588                 this.store.load();
28589         },
28590
28591 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28592         getValue: function() {
28593                 var result = '(';
28594                 this.store.each(function(rec) {
28595                         result += rec.id + ',';
28596                 });
28597                 return result.substr(0, result.length - 1) + ')';
28598         },
28599         
28600         getIds: function() {
28601                 var i = 0, result = new Array(this.store.getCount());
28602                 this.store.each(function(rec) {
28603                         result[i++] = rec.id;
28604                 });
28605                 return result;
28606         },
28607         
28608         isDirty: function() {
28609                 return this.isDirtyFlag;
28610         },
28611
28612 /**
28613  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28614  *      whole Element becomes the target, and this causes the drop gesture to append.
28615  */
28616     getTargetFromEvent : function(e) {
28617                 var target = e.getTarget();
28618                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28619                 target = target.parentNode;
28620                 }
28621                 if (!target) {
28622                         target = this.el.dom.lastChild || this.el.dom;
28623                 }
28624                 return target;
28625     },
28626
28627 /**
28628  *      Create the drag data which consists of an object which has the property "ddel" as
28629  *      the drag proxy element. 
28630  */
28631     getDragData : function(e) {
28632         var target = this.findItemFromChild(e.getTarget());
28633                 if(target) {
28634                         this.handleSelection(e);
28635                         var selNodes = this.getSelectedNodes();
28636             var dragData = {
28637                 source: this,
28638                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28639                 nodes: selNodes,
28640                 records: []
28641                         };
28642                         var selectedIndices = this.getSelectedIndexes();
28643                         for (var i = 0; i < selectedIndices.length; i++) {
28644                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28645                         }
28646                         if (selNodes.length == 1) {
28647                                 dragData.ddel = target.cloneNode(true); // the div element
28648                         } else {
28649                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28650                                 div.className = 'multi-proxy';
28651                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28652                                         div.appendChild(selNodes[i].cloneNode(true));
28653                                 }
28654                                 dragData.ddel = div;
28655                         }
28656             //console.log(dragData)
28657             //console.log(dragData.ddel.innerHTML)
28658                         return dragData;
28659                 }
28660         //console.log('nodragData')
28661                 return false;
28662     },
28663     
28664 /**     Specify to which ddGroup items in this DDView may be dragged. */
28665     setDraggable: function(ddGroup) {
28666         if (ddGroup instanceof Array) {
28667                 Roo.each(ddGroup, this.setDraggable, this);
28668                 return;
28669         }
28670         if (this.dragZone) {
28671                 this.dragZone.addToGroup(ddGroup);
28672         } else {
28673                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28674                                 containerScroll: true,
28675                                 ddGroup: ddGroup 
28676
28677                         });
28678 //                      Draggability implies selection. DragZone's mousedown selects the element.
28679                         if (!this.multiSelect) { this.singleSelect = true; }
28680
28681 //                      Wire the DragZone's handlers up to methods in *this*
28682                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28683                 }
28684     },
28685
28686 /**     Specify from which ddGroup this DDView accepts drops. */
28687     setDroppable: function(ddGroup) {
28688         if (ddGroup instanceof Array) {
28689                 Roo.each(ddGroup, this.setDroppable, this);
28690                 return;
28691         }
28692         if (this.dropZone) {
28693                 this.dropZone.addToGroup(ddGroup);
28694         } else {
28695                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28696                                 containerScroll: true,
28697                                 ddGroup: ddGroup
28698                         });
28699
28700 //                      Wire the DropZone's handlers up to methods in *this*
28701                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28702                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28703                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28704                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28705                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28706                 }
28707     },
28708
28709 /**     Decide whether to drop above or below a View node. */
28710     getDropPoint : function(e, n, dd){
28711         if (n == this.el.dom) { return "above"; }
28712                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28713                 var c = t + (b - t) / 2;
28714                 var y = Roo.lib.Event.getPageY(e);
28715                 if(y <= c) {
28716                         return "above";
28717                 }else{
28718                         return "below";
28719                 }
28720     },
28721
28722     onNodeEnter : function(n, dd, e, data){
28723                 return false;
28724     },
28725     
28726     onNodeOver : function(n, dd, e, data){
28727                 var pt = this.getDropPoint(e, n, dd);
28728                 // set the insert point style on the target node
28729                 var dragElClass = this.dropNotAllowed;
28730                 if (pt) {
28731                         var targetElClass;
28732                         if (pt == "above"){
28733                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28734                                 targetElClass = "x-view-drag-insert-above";
28735                         } else {
28736                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28737                                 targetElClass = "x-view-drag-insert-below";
28738                         }
28739                         if (this.lastInsertClass != targetElClass){
28740                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28741                                 this.lastInsertClass = targetElClass;
28742                         }
28743                 }
28744                 return dragElClass;
28745         },
28746
28747     onNodeOut : function(n, dd, e, data){
28748                 this.removeDropIndicators(n);
28749     },
28750
28751     onNodeDrop : function(n, dd, e, data){
28752         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28753                 return false;
28754         }
28755         var pt = this.getDropPoint(e, n, dd);
28756                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28757                 if (pt == "below") { insertAt++; }
28758                 for (var i = 0; i < data.records.length; i++) {
28759                         var r = data.records[i];
28760                         var dup = this.store.getById(r.id);
28761                         if (dup && (dd != this.dragZone)) {
28762                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28763                         } else {
28764                                 if (data.copy) {
28765                                         this.store.insert(insertAt++, r.copy());
28766                                 } else {
28767                                         data.source.isDirtyFlag = true;
28768                                         r.store.remove(r);
28769                                         this.store.insert(insertAt++, r);
28770                                 }
28771                                 this.isDirtyFlag = true;
28772                         }
28773                 }
28774                 this.dragZone.cachedTarget = null;
28775                 return true;
28776     },
28777
28778     removeDropIndicators : function(n){
28779                 if(n){
28780                         Roo.fly(n).removeClass([
28781                                 "x-view-drag-insert-above",
28782                                 "x-view-drag-insert-below"]);
28783                         this.lastInsertClass = "_noclass";
28784                 }
28785     },
28786
28787 /**
28788  *      Utility method. Add a delete option to the DDView's context menu.
28789  *      @param {String} imageUrl The URL of the "delete" icon image.
28790  */
28791         setDeletable: function(imageUrl) {
28792                 if (!this.singleSelect && !this.multiSelect) {
28793                         this.singleSelect = true;
28794                 }
28795                 var c = this.getContextMenu();
28796                 this.contextMenu.on("itemclick", function(item) {
28797                         switch (item.id) {
28798                                 case "delete":
28799                                         this.remove(this.getSelectedIndexes());
28800                                         break;
28801                         }
28802                 }, this);
28803                 this.contextMenu.add({
28804                         icon: imageUrl,
28805                         id: "delete",
28806                         text: 'Delete'
28807                 });
28808         },
28809         
28810 /**     Return the context menu for this DDView. */
28811         getContextMenu: function() {
28812                 if (!this.contextMenu) {
28813 //                      Create the View's context menu
28814                         this.contextMenu = new Roo.menu.Menu({
28815                                 id: this.id + "-contextmenu"
28816                         });
28817                         this.el.on("contextmenu", this.showContextMenu, this);
28818                 }
28819                 return this.contextMenu;
28820         },
28821         
28822         disableContextMenu: function() {
28823                 if (this.contextMenu) {
28824                         this.el.un("contextmenu", this.showContextMenu, this);
28825                 }
28826         },
28827
28828         showContextMenu: function(e, item) {
28829         item = this.findItemFromChild(e.getTarget());
28830                 if (item) {
28831                         e.stopEvent();
28832                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28833                         this.contextMenu.showAt(e.getXY());
28834             }
28835     },
28836
28837 /**
28838  *      Remove {@link Roo.data.Record}s at the specified indices.
28839  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28840  */
28841     remove: function(selectedIndices) {
28842                 selectedIndices = [].concat(selectedIndices);
28843                 for (var i = 0; i < selectedIndices.length; i++) {
28844                         var rec = this.store.getAt(selectedIndices[i]);
28845                         this.store.remove(rec);
28846                 }
28847     },
28848
28849 /**
28850  *      Double click fires the event, but also, if this is draggable, and there is only one other
28851  *      related DropZone, it transfers the selected node.
28852  */
28853     onDblClick : function(e){
28854         var item = this.findItemFromChild(e.getTarget());
28855         if(item){
28856             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28857                 return false;
28858             }
28859             if (this.dragGroup) {
28860                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28861                     while (targets.indexOf(this.dropZone) > -1) {
28862                             targets.remove(this.dropZone);
28863                                 }
28864                     if (targets.length == 1) {
28865                                         this.dragZone.cachedTarget = null;
28866                         var el = Roo.get(targets[0].getEl());
28867                         var box = el.getBox(true);
28868                         targets[0].onNodeDrop(el.dom, {
28869                                 target: el.dom,
28870                                 xy: [box.x, box.y + box.height - 1]
28871                         }, null, this.getDragData(e));
28872                     }
28873                 }
28874         }
28875     },
28876     
28877     handleSelection: function(e) {
28878                 this.dragZone.cachedTarget = null;
28879         var item = this.findItemFromChild(e.getTarget());
28880         if (!item) {
28881                 this.clearSelections(true);
28882                 return;
28883         }
28884                 if (item && (this.multiSelect || this.singleSelect)){
28885                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28886                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28887                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28888                                 this.unselect(item);
28889                         } else {
28890                                 this.select(item, this.multiSelect && e.ctrlKey);
28891                                 this.lastSelection = item;
28892                         }
28893                 }
28894     },
28895
28896     onItemClick : function(item, index, e){
28897                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28898                         return false;
28899                 }
28900                 return true;
28901     },
28902
28903     unselect : function(nodeInfo, suppressEvent){
28904                 var node = this.getNode(nodeInfo);
28905                 if(node && this.isSelected(node)){
28906                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28907                                 Roo.fly(node).removeClass(this.selectedClass);
28908                                 this.selections.remove(node);
28909                                 if(!suppressEvent){
28910                                         this.fireEvent("selectionchange", this, this.selections);
28911                                 }
28912                         }
28913                 }
28914     }
28915 });
28916 /*
28917  * Based on:
28918  * Ext JS Library 1.1.1
28919  * Copyright(c) 2006-2007, Ext JS, LLC.
28920  *
28921  * Originally Released Under LGPL - original licence link has changed is not relivant.
28922  *
28923  * Fork - LGPL
28924  * <script type="text/javascript">
28925  */
28926  
28927 /**
28928  * @class Roo.LayoutManager
28929  * @extends Roo.util.Observable
28930  * Base class for layout managers.
28931  */
28932 Roo.LayoutManager = function(container, config){
28933     Roo.LayoutManager.superclass.constructor.call(this);
28934     this.el = Roo.get(container);
28935     // ie scrollbar fix
28936     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28937         document.body.scroll = "no";
28938     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28939         this.el.position('relative');
28940     }
28941     this.id = this.el.id;
28942     this.el.addClass("x-layout-container");
28943     /** false to disable window resize monitoring @type Boolean */
28944     this.monitorWindowResize = true;
28945     this.regions = {};
28946     this.addEvents({
28947         /**
28948          * @event layout
28949          * Fires when a layout is performed. 
28950          * @param {Roo.LayoutManager} this
28951          */
28952         "layout" : true,
28953         /**
28954          * @event regionresized
28955          * Fires when the user resizes a region. 
28956          * @param {Roo.LayoutRegion} region The resized region
28957          * @param {Number} newSize The new size (width for east/west, height for north/south)
28958          */
28959         "regionresized" : true,
28960         /**
28961          * @event regioncollapsed
28962          * Fires when a region is collapsed. 
28963          * @param {Roo.LayoutRegion} region The collapsed region
28964          */
28965         "regioncollapsed" : true,
28966         /**
28967          * @event regionexpanded
28968          * Fires when a region is expanded.  
28969          * @param {Roo.LayoutRegion} region The expanded region
28970          */
28971         "regionexpanded" : true
28972     });
28973     this.updating = false;
28974     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28975 };
28976
28977 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28978     /**
28979      * Returns true if this layout is currently being updated
28980      * @return {Boolean}
28981      */
28982     isUpdating : function(){
28983         return this.updating; 
28984     },
28985     
28986     /**
28987      * Suspend the LayoutManager from doing auto-layouts while
28988      * making multiple add or remove calls
28989      */
28990     beginUpdate : function(){
28991         this.updating = true;    
28992     },
28993     
28994     /**
28995      * Restore auto-layouts and optionally disable the manager from performing a layout
28996      * @param {Boolean} noLayout true to disable a layout update 
28997      */
28998     endUpdate : function(noLayout){
28999         this.updating = false;
29000         if(!noLayout){
29001             this.layout();
29002         }    
29003     },
29004     
29005     layout: function(){
29006         
29007     },
29008     
29009     onRegionResized : function(region, newSize){
29010         this.fireEvent("regionresized", region, newSize);
29011         this.layout();
29012     },
29013     
29014     onRegionCollapsed : function(region){
29015         this.fireEvent("regioncollapsed", region);
29016     },
29017     
29018     onRegionExpanded : function(region){
29019         this.fireEvent("regionexpanded", region);
29020     },
29021         
29022     /**
29023      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
29024      * performs box-model adjustments.
29025      * @return {Object} The size as an object {width: (the width), height: (the height)}
29026      */
29027     getViewSize : function(){
29028         var size;
29029         if(this.el.dom != document.body){
29030             size = this.el.getSize();
29031         }else{
29032             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
29033         }
29034         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
29035         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29036         return size;
29037     },
29038     
29039     /**
29040      * Returns the Element this layout is bound to.
29041      * @return {Roo.Element}
29042      */
29043     getEl : function(){
29044         return this.el;
29045     },
29046     
29047     /**
29048      * Returns the specified region.
29049      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
29050      * @return {Roo.LayoutRegion}
29051      */
29052     getRegion : function(target){
29053         return this.regions[target.toLowerCase()];
29054     },
29055     
29056     onWindowResize : function(){
29057         if(this.monitorWindowResize){
29058             this.layout();
29059         }
29060     }
29061 });/*
29062  * Based on:
29063  * Ext JS Library 1.1.1
29064  * Copyright(c) 2006-2007, Ext JS, LLC.
29065  *
29066  * Originally Released Under LGPL - original licence link has changed is not relivant.
29067  *
29068  * Fork - LGPL
29069  * <script type="text/javascript">
29070  */
29071 /**
29072  * @class Roo.BorderLayout
29073  * @extends Roo.LayoutManager
29074  * @children Roo.ContentPanel
29075  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
29076  * please see: <br><br>
29077  * <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>
29078  * <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>
29079  * Example:
29080  <pre><code>
29081  var layout = new Roo.BorderLayout(document.body, {
29082     north: {
29083         initialSize: 25,
29084         titlebar: false
29085     },
29086     west: {
29087         split:true,
29088         initialSize: 200,
29089         minSize: 175,
29090         maxSize: 400,
29091         titlebar: true,
29092         collapsible: true
29093     },
29094     east: {
29095         split:true,
29096         initialSize: 202,
29097         minSize: 175,
29098         maxSize: 400,
29099         titlebar: true,
29100         collapsible: true
29101     },
29102     south: {
29103         split:true,
29104         initialSize: 100,
29105         minSize: 100,
29106         maxSize: 200,
29107         titlebar: true,
29108         collapsible: true
29109     },
29110     center: {
29111         titlebar: true,
29112         autoScroll:true,
29113         resizeTabs: true,
29114         minTabWidth: 50,
29115         preferredTabWidth: 150
29116     }
29117 });
29118
29119 // shorthand
29120 var CP = Roo.ContentPanel;
29121
29122 layout.beginUpdate();
29123 layout.add("north", new CP("north", "North"));
29124 layout.add("south", new CP("south", {title: "South", closable: true}));
29125 layout.add("west", new CP("west", {title: "West"}));
29126 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29127 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29128 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29129 layout.getRegion("center").showPanel("center1");
29130 layout.endUpdate();
29131 </code></pre>
29132
29133 <b>The container the layout is rendered into can be either the body element or any other element.
29134 If it is not the body element, the container needs to either be an absolute positioned element,
29135 or you will need to add "position:relative" to the css of the container.  You will also need to specify
29136 the container size if it is not the body element.</b>
29137
29138 * @constructor
29139 * Create a new BorderLayout
29140 * @param {String/HTMLElement/Element} container The container this layout is bound to
29141 * @param {Object} config Configuration options
29142  */
29143 Roo.BorderLayout = function(container, config){
29144     config = config || {};
29145     Roo.BorderLayout.superclass.constructor.call(this, container, config);
29146     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29147     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29148         var target = this.factory.validRegions[i];
29149         if(config[target]){
29150             this.addRegion(target, config[target]);
29151         }
29152     }
29153 };
29154
29155 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29156         
29157         /**
29158          * @cfg {Roo.LayoutRegion} east
29159          */
29160         /**
29161          * @cfg {Roo.LayoutRegion} west
29162          */
29163         /**
29164          * @cfg {Roo.LayoutRegion} north
29165          */
29166         /**
29167          * @cfg {Roo.LayoutRegion} south
29168          */
29169         /**
29170          * @cfg {Roo.LayoutRegion} center
29171          */
29172     /**
29173      * Creates and adds a new region if it doesn't already exist.
29174      * @param {String} target The target region key (north, south, east, west or center).
29175      * @param {Object} config The regions config object
29176      * @return {BorderLayoutRegion} The new region
29177      */
29178     addRegion : function(target, config){
29179         if(!this.regions[target]){
29180             var r = this.factory.create(target, this, config);
29181             this.bindRegion(target, r);
29182         }
29183         return this.regions[target];
29184     },
29185
29186     // private (kinda)
29187     bindRegion : function(name, r){
29188         this.regions[name] = r;
29189         r.on("visibilitychange", this.layout, this);
29190         r.on("paneladded", this.layout, this);
29191         r.on("panelremoved", this.layout, this);
29192         r.on("invalidated", this.layout, this);
29193         r.on("resized", this.onRegionResized, this);
29194         r.on("collapsed", this.onRegionCollapsed, this);
29195         r.on("expanded", this.onRegionExpanded, this);
29196     },
29197
29198     /**
29199      * Performs a layout update.
29200      */
29201     layout : function(){
29202         if(this.updating) {
29203             return;
29204         }
29205         var size = this.getViewSize();
29206         var w = size.width;
29207         var h = size.height;
29208         var centerW = w;
29209         var centerH = h;
29210         var centerY = 0;
29211         var centerX = 0;
29212         //var x = 0, y = 0;
29213
29214         var rs = this.regions;
29215         var north = rs["north"];
29216         var south = rs["south"]; 
29217         var west = rs["west"];
29218         var east = rs["east"];
29219         var center = rs["center"];
29220         //if(this.hideOnLayout){ // not supported anymore
29221             //c.el.setStyle("display", "none");
29222         //}
29223         if(north && north.isVisible()){
29224             var b = north.getBox();
29225             var m = north.getMargins();
29226             b.width = w - (m.left+m.right);
29227             b.x = m.left;
29228             b.y = m.top;
29229             centerY = b.height + b.y + m.bottom;
29230             centerH -= centerY;
29231             north.updateBox(this.safeBox(b));
29232         }
29233         if(south && south.isVisible()){
29234             var b = south.getBox();
29235             var m = south.getMargins();
29236             b.width = w - (m.left+m.right);
29237             b.x = m.left;
29238             var totalHeight = (b.height + m.top + m.bottom);
29239             b.y = h - totalHeight + m.top;
29240             centerH -= totalHeight;
29241             south.updateBox(this.safeBox(b));
29242         }
29243         if(west && west.isVisible()){
29244             var b = west.getBox();
29245             var m = west.getMargins();
29246             b.height = centerH - (m.top+m.bottom);
29247             b.x = m.left;
29248             b.y = centerY + m.top;
29249             var totalWidth = (b.width + m.left + m.right);
29250             centerX += totalWidth;
29251             centerW -= totalWidth;
29252             west.updateBox(this.safeBox(b));
29253         }
29254         if(east && east.isVisible()){
29255             var b = east.getBox();
29256             var m = east.getMargins();
29257             b.height = centerH - (m.top+m.bottom);
29258             var totalWidth = (b.width + m.left + m.right);
29259             b.x = w - totalWidth + m.left;
29260             b.y = centerY + m.top;
29261             centerW -= totalWidth;
29262             east.updateBox(this.safeBox(b));
29263         }
29264         if(center){
29265             var m = center.getMargins();
29266             var centerBox = {
29267                 x: centerX + m.left,
29268                 y: centerY + m.top,
29269                 width: centerW - (m.left+m.right),
29270                 height: centerH - (m.top+m.bottom)
29271             };
29272             //if(this.hideOnLayout){
29273                 //center.el.setStyle("display", "block");
29274             //}
29275             center.updateBox(this.safeBox(centerBox));
29276         }
29277         this.el.repaint();
29278         this.fireEvent("layout", this);
29279     },
29280
29281     // private
29282     safeBox : function(box){
29283         box.width = Math.max(0, box.width);
29284         box.height = Math.max(0, box.height);
29285         return box;
29286     },
29287
29288     /**
29289      * Adds a ContentPanel (or subclass) to this layout.
29290      * @param {String} target The target region key (north, south, east, west or center).
29291      * @param {Roo.ContentPanel} panel The panel to add
29292      * @return {Roo.ContentPanel} The added panel
29293      */
29294     add : function(target, panel){
29295          
29296         target = target.toLowerCase();
29297         return this.regions[target].add(panel);
29298     },
29299
29300     /**
29301      * Remove a ContentPanel (or subclass) to this layout.
29302      * @param {String} target The target region key (north, south, east, west or center).
29303      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29304      * @return {Roo.ContentPanel} The removed panel
29305      */
29306     remove : function(target, panel){
29307         target = target.toLowerCase();
29308         return this.regions[target].remove(panel);
29309     },
29310
29311     /**
29312      * Searches all regions for a panel with the specified id
29313      * @param {String} panelId
29314      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29315      */
29316     findPanel : function(panelId){
29317         var rs = this.regions;
29318         for(var target in rs){
29319             if(typeof rs[target] != "function"){
29320                 var p = rs[target].getPanel(panelId);
29321                 if(p){
29322                     return p;
29323                 }
29324             }
29325         }
29326         return null;
29327     },
29328
29329     /**
29330      * Searches all regions for a panel with the specified id and activates (shows) it.
29331      * @param {String/ContentPanel} panelId The panels id or the panel itself
29332      * @return {Roo.ContentPanel} The shown panel or null
29333      */
29334     showPanel : function(panelId) {
29335       var rs = this.regions;
29336       for(var target in rs){
29337          var r = rs[target];
29338          if(typeof r != "function"){
29339             if(r.hasPanel(panelId)){
29340                return r.showPanel(panelId);
29341             }
29342          }
29343       }
29344       return null;
29345    },
29346
29347    /**
29348      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29349      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29350      */
29351     restoreState : function(provider){
29352         if(!provider){
29353             provider = Roo.state.Manager;
29354         }
29355         var sm = new Roo.LayoutStateManager();
29356         sm.init(this, provider);
29357     },
29358
29359     /**
29360      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29361      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29362      * a valid ContentPanel config object.  Example:
29363      * <pre><code>
29364 // Create the main layout
29365 var layout = new Roo.BorderLayout('main-ct', {
29366     west: {
29367         split:true,
29368         minSize: 175,
29369         titlebar: true
29370     },
29371     center: {
29372         title:'Components'
29373     }
29374 }, 'main-ct');
29375
29376 // Create and add multiple ContentPanels at once via configs
29377 layout.batchAdd({
29378    west: {
29379        id: 'source-files',
29380        autoCreate:true,
29381        title:'Ext Source Files',
29382        autoScroll:true,
29383        fitToFrame:true
29384    },
29385    center : {
29386        el: cview,
29387        autoScroll:true,
29388        fitToFrame:true,
29389        toolbar: tb,
29390        resizeEl:'cbody'
29391    }
29392 });
29393 </code></pre>
29394      * @param {Object} regions An object containing ContentPanel configs by region name
29395      */
29396     batchAdd : function(regions){
29397         this.beginUpdate();
29398         for(var rname in regions){
29399             var lr = this.regions[rname];
29400             if(lr){
29401                 this.addTypedPanels(lr, regions[rname]);
29402             }
29403         }
29404         this.endUpdate();
29405     },
29406
29407     // private
29408     addTypedPanels : function(lr, ps){
29409         if(typeof ps == 'string'){
29410             lr.add(new Roo.ContentPanel(ps));
29411         }
29412         else if(ps instanceof Array){
29413             for(var i =0, len = ps.length; i < len; i++){
29414                 this.addTypedPanels(lr, ps[i]);
29415             }
29416         }
29417         else if(!ps.events){ // raw config?
29418             var el = ps.el;
29419             delete ps.el; // prevent conflict
29420             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29421         }
29422         else {  // panel object assumed!
29423             lr.add(ps);
29424         }
29425     },
29426     /**
29427      * Adds a xtype elements to the layout.
29428      * <pre><code>
29429
29430 layout.addxtype({
29431        xtype : 'ContentPanel',
29432        region: 'west',
29433        items: [ .... ]
29434    }
29435 );
29436
29437 layout.addxtype({
29438         xtype : 'NestedLayoutPanel',
29439         region: 'west',
29440         layout: {
29441            center: { },
29442            west: { }   
29443         },
29444         items : [ ... list of content panels or nested layout panels.. ]
29445    }
29446 );
29447 </code></pre>
29448      * @param {Object} cfg Xtype definition of item to add.
29449      */
29450     addxtype : function(cfg)
29451     {
29452         // basically accepts a pannel...
29453         // can accept a layout region..!?!?
29454         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
29455         
29456         if (!cfg.xtype.match(/Panel$/)) {
29457             return false;
29458         }
29459         var ret = false;
29460         
29461         if (typeof(cfg.region) == 'undefined') {
29462             Roo.log("Failed to add Panel, region was not set");
29463             Roo.log(cfg);
29464             return false;
29465         }
29466         var region = cfg.region;
29467         delete cfg.region;
29468         
29469           
29470         var xitems = [];
29471         if (cfg.items) {
29472             xitems = cfg.items;
29473             delete cfg.items;
29474         }
29475         var nb = false;
29476         
29477         switch(cfg.xtype) 
29478         {
29479             case 'ContentPanel':  // ContentPanel (el, cfg)
29480             case 'ScrollPanel':  // ContentPanel (el, cfg)
29481             case 'ViewPanel': 
29482                 if(cfg.autoCreate) {
29483                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29484                 } else {
29485                     var el = this.el.createChild();
29486                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29487                 }
29488                 
29489                 this.add(region, ret);
29490                 break;
29491             
29492             
29493             case 'TreePanel': // our new panel!
29494                 cfg.el = this.el.createChild();
29495                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29496                 this.add(region, ret);
29497                 break;
29498             
29499             case 'NestedLayoutPanel': 
29500                 // create a new Layout (which is  a Border Layout...
29501                 var el = this.el.createChild();
29502                 var clayout = cfg.layout;
29503                 delete cfg.layout;
29504                 clayout.items   = clayout.items  || [];
29505                 // replace this exitems with the clayout ones..
29506                 xitems = clayout.items;
29507                  
29508                 
29509                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29510                     cfg.background = false;
29511                 }
29512                 var layout = new Roo.BorderLayout(el, clayout);
29513                 
29514                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29515                 //console.log('adding nested layout panel '  + cfg.toSource());
29516                 this.add(region, ret);
29517                 nb = {}; /// find first...
29518                 break;
29519                 
29520             case 'GridPanel': 
29521             
29522                 // needs grid and region
29523                 
29524                 //var el = this.getRegion(region).el.createChild();
29525                 var el = this.el.createChild();
29526                 // create the grid first...
29527                 
29528                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29529                 delete cfg.grid;
29530                 if (region == 'center' && this.active ) {
29531                     cfg.background = false;
29532                 }
29533                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29534                 
29535                 this.add(region, ret);
29536                 if (cfg.background) {
29537                     ret.on('activate', function(gp) {
29538                         if (!gp.grid.rendered) {
29539                             gp.grid.render();
29540                         }
29541                     });
29542                 } else {
29543                     grid.render();
29544                 }
29545                 break;
29546            
29547            
29548            
29549                 
29550                 
29551                 
29552             default:
29553                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29554                     
29555                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29556                     this.add(region, ret);
29557                 } else {
29558                 
29559                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29560                     return null;
29561                 }
29562                 
29563              // GridPanel (grid, cfg)
29564             
29565         }
29566         this.beginUpdate();
29567         // add children..
29568         var region = '';
29569         var abn = {};
29570         Roo.each(xitems, function(i)  {
29571             region = nb && i.region ? i.region : false;
29572             
29573             var add = ret.addxtype(i);
29574            
29575             if (region) {
29576                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29577                 if (!i.background) {
29578                     abn[region] = nb[region] ;
29579                 }
29580             }
29581             
29582         });
29583         this.endUpdate();
29584
29585         // make the last non-background panel active..
29586         //if (nb) { Roo.log(abn); }
29587         if (nb) {
29588             
29589             for(var r in abn) {
29590                 region = this.getRegion(r);
29591                 if (region) {
29592                     // tried using nb[r], but it does not work..
29593                      
29594                     region.showPanel(abn[r]);
29595                    
29596                 }
29597             }
29598         }
29599         return ret;
29600         
29601     }
29602 });
29603
29604 /**
29605  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29606  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29607  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29608  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29609  * <pre><code>
29610 // shorthand
29611 var CP = Roo.ContentPanel;
29612
29613 var layout = Roo.BorderLayout.create({
29614     north: {
29615         initialSize: 25,
29616         titlebar: false,
29617         panels: [new CP("north", "North")]
29618     },
29619     west: {
29620         split:true,
29621         initialSize: 200,
29622         minSize: 175,
29623         maxSize: 400,
29624         titlebar: true,
29625         collapsible: true,
29626         panels: [new CP("west", {title: "West"})]
29627     },
29628     east: {
29629         split:true,
29630         initialSize: 202,
29631         minSize: 175,
29632         maxSize: 400,
29633         titlebar: true,
29634         collapsible: true,
29635         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29636     },
29637     south: {
29638         split:true,
29639         initialSize: 100,
29640         minSize: 100,
29641         maxSize: 200,
29642         titlebar: true,
29643         collapsible: true,
29644         panels: [new CP("south", {title: "South", closable: true})]
29645     },
29646     center: {
29647         titlebar: true,
29648         autoScroll:true,
29649         resizeTabs: true,
29650         minTabWidth: 50,
29651         preferredTabWidth: 150,
29652         panels: [
29653             new CP("center1", {title: "Close Me", closable: true}),
29654             new CP("center2", {title: "Center Panel", closable: false})
29655         ]
29656     }
29657 }, document.body);
29658
29659 layout.getRegion("center").showPanel("center1");
29660 </code></pre>
29661  * @param config
29662  * @param targetEl
29663  */
29664 Roo.BorderLayout.create = function(config, targetEl){
29665     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29666     layout.beginUpdate();
29667     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29668     for(var j = 0, jlen = regions.length; j < jlen; j++){
29669         var lr = regions[j];
29670         if(layout.regions[lr] && config[lr].panels){
29671             var r = layout.regions[lr];
29672             var ps = config[lr].panels;
29673             layout.addTypedPanels(r, ps);
29674         }
29675     }
29676     layout.endUpdate();
29677     return layout;
29678 };
29679
29680 // private
29681 Roo.BorderLayout.RegionFactory = {
29682     // private
29683     validRegions : ["north","south","east","west","center"],
29684
29685     // private
29686     create : function(target, mgr, config){
29687         target = target.toLowerCase();
29688         if(config.lightweight || config.basic){
29689             return new Roo.BasicLayoutRegion(mgr, config, target);
29690         }
29691         switch(target){
29692             case "north":
29693                 return new Roo.NorthLayoutRegion(mgr, config);
29694             case "south":
29695                 return new Roo.SouthLayoutRegion(mgr, config);
29696             case "east":
29697                 return new Roo.EastLayoutRegion(mgr, config);
29698             case "west":
29699                 return new Roo.WestLayoutRegion(mgr, config);
29700             case "center":
29701                 return new Roo.CenterLayoutRegion(mgr, config);
29702         }
29703         throw 'Layout region "'+target+'" not supported.';
29704     }
29705 };/*
29706  * Based on:
29707  * Ext JS Library 1.1.1
29708  * Copyright(c) 2006-2007, Ext JS, LLC.
29709  *
29710  * Originally Released Under LGPL - original licence link has changed is not relivant.
29711  *
29712  * Fork - LGPL
29713  * <script type="text/javascript">
29714  */
29715  
29716 /**
29717  * @class Roo.BasicLayoutRegion
29718  * @extends Roo.util.Observable
29719  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29720  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29721  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29722  */
29723 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29724     this.mgr = mgr;
29725     this.position  = pos;
29726     this.events = {
29727         /**
29728          * @scope Roo.BasicLayoutRegion
29729          */
29730         
29731         /**
29732          * @event beforeremove
29733          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29734          * @param {Roo.LayoutRegion} this
29735          * @param {Roo.ContentPanel} panel The panel
29736          * @param {Object} e The cancel event object
29737          */
29738         "beforeremove" : true,
29739         /**
29740          * @event invalidated
29741          * Fires when the layout for this region is changed.
29742          * @param {Roo.LayoutRegion} this
29743          */
29744         "invalidated" : true,
29745         /**
29746          * @event visibilitychange
29747          * Fires when this region is shown or hidden 
29748          * @param {Roo.LayoutRegion} this
29749          * @param {Boolean} visibility true or false
29750          */
29751         "visibilitychange" : true,
29752         /**
29753          * @event paneladded
29754          * Fires when a panel is added. 
29755          * @param {Roo.LayoutRegion} this
29756          * @param {Roo.ContentPanel} panel The panel
29757          */
29758         "paneladded" : true,
29759         /**
29760          * @event panelremoved
29761          * Fires when a panel is removed. 
29762          * @param {Roo.LayoutRegion} this
29763          * @param {Roo.ContentPanel} panel The panel
29764          */
29765         "panelremoved" : true,
29766         /**
29767          * @event beforecollapse
29768          * Fires when this region before collapse.
29769          * @param {Roo.LayoutRegion} this
29770          */
29771         "beforecollapse" : true,
29772         /**
29773          * @event collapsed
29774          * Fires when this region is collapsed.
29775          * @param {Roo.LayoutRegion} this
29776          */
29777         "collapsed" : true,
29778         /**
29779          * @event expanded
29780          * Fires when this region is expanded.
29781          * @param {Roo.LayoutRegion} this
29782          */
29783         "expanded" : true,
29784         /**
29785          * @event slideshow
29786          * Fires when this region is slid into view.
29787          * @param {Roo.LayoutRegion} this
29788          */
29789         "slideshow" : true,
29790         /**
29791          * @event slidehide
29792          * Fires when this region slides out of view. 
29793          * @param {Roo.LayoutRegion} this
29794          */
29795         "slidehide" : true,
29796         /**
29797          * @event panelactivated
29798          * Fires when a panel is activated. 
29799          * @param {Roo.LayoutRegion} this
29800          * @param {Roo.ContentPanel} panel The activated panel
29801          */
29802         "panelactivated" : true,
29803         /**
29804          * @event resized
29805          * Fires when the user resizes this region. 
29806          * @param {Roo.LayoutRegion} this
29807          * @param {Number} newSize The new size (width for east/west, height for north/south)
29808          */
29809         "resized" : true
29810     };
29811     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29812     this.panels = new Roo.util.MixedCollection();
29813     this.panels.getKey = this.getPanelId.createDelegate(this);
29814     this.box = null;
29815     this.activePanel = null;
29816     // ensure listeners are added...
29817     
29818     if (config.listeners || config.events) {
29819         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29820             listeners : config.listeners || {},
29821             events : config.events || {}
29822         });
29823     }
29824     
29825     if(skipConfig !== true){
29826         this.applyConfig(config);
29827     }
29828 };
29829
29830 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29831     getPanelId : function(p){
29832         return p.getId();
29833     },
29834     
29835     applyConfig : function(config){
29836         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29837         this.config = config;
29838         
29839     },
29840     
29841     /**
29842      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29843      * the width, for horizontal (north, south) the height.
29844      * @param {Number} newSize The new width or height
29845      */
29846     resizeTo : function(newSize){
29847         var el = this.el ? this.el :
29848                  (this.activePanel ? this.activePanel.getEl() : null);
29849         if(el){
29850             switch(this.position){
29851                 case "east":
29852                 case "west":
29853                     el.setWidth(newSize);
29854                     this.fireEvent("resized", this, newSize);
29855                 break;
29856                 case "north":
29857                 case "south":
29858                     el.setHeight(newSize);
29859                     this.fireEvent("resized", this, newSize);
29860                 break;                
29861             }
29862         }
29863     },
29864     
29865     getBox : function(){
29866         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29867     },
29868     
29869     getMargins : function(){
29870         return this.margins;
29871     },
29872     
29873     updateBox : function(box){
29874         this.box = box;
29875         var el = this.activePanel.getEl();
29876         el.dom.style.left = box.x + "px";
29877         el.dom.style.top = box.y + "px";
29878         this.activePanel.setSize(box.width, box.height);
29879     },
29880     
29881     /**
29882      * Returns the container element for this region.
29883      * @return {Roo.Element}
29884      */
29885     getEl : function(){
29886         return this.activePanel;
29887     },
29888     
29889     /**
29890      * Returns true if this region is currently visible.
29891      * @return {Boolean}
29892      */
29893     isVisible : function(){
29894         return this.activePanel ? true : false;
29895     },
29896     
29897     setActivePanel : function(panel){
29898         panel = this.getPanel(panel);
29899         if(this.activePanel && this.activePanel != panel){
29900             this.activePanel.setActiveState(false);
29901             this.activePanel.getEl().setLeftTop(-10000,-10000);
29902         }
29903         this.activePanel = panel;
29904         panel.setActiveState(true);
29905         if(this.box){
29906             panel.setSize(this.box.width, this.box.height);
29907         }
29908         this.fireEvent("panelactivated", this, panel);
29909         this.fireEvent("invalidated");
29910     },
29911     
29912     /**
29913      * Show the specified panel.
29914      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29915      * @return {Roo.ContentPanel} The shown panel or null
29916      */
29917     showPanel : function(panel){
29918         if(panel = this.getPanel(panel)){
29919             this.setActivePanel(panel);
29920         }
29921         return panel;
29922     },
29923     
29924     /**
29925      * Get the active panel for this region.
29926      * @return {Roo.ContentPanel} The active panel or null
29927      */
29928     getActivePanel : function(){
29929         return this.activePanel;
29930     },
29931     
29932     /**
29933      * Add the passed ContentPanel(s)
29934      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29935      * @return {Roo.ContentPanel} The panel added (if only one was added)
29936      */
29937     add : function(panel){
29938         if(arguments.length > 1){
29939             for(var i = 0, len = arguments.length; i < len; i++) {
29940                 this.add(arguments[i]);
29941             }
29942             return null;
29943         }
29944         if(this.hasPanel(panel)){
29945             this.showPanel(panel);
29946             return panel;
29947         }
29948         var el = panel.getEl();
29949         if(el.dom.parentNode != this.mgr.el.dom){
29950             this.mgr.el.dom.appendChild(el.dom);
29951         }
29952         if(panel.setRegion){
29953             panel.setRegion(this);
29954         }
29955         this.panels.add(panel);
29956         el.setStyle("position", "absolute");
29957         if(!panel.background){
29958             this.setActivePanel(panel);
29959             if(this.config.initialSize && this.panels.getCount()==1){
29960                 this.resizeTo(this.config.initialSize);
29961             }
29962         }
29963         this.fireEvent("paneladded", this, panel);
29964         return panel;
29965     },
29966     
29967     /**
29968      * Returns true if the panel is in this region.
29969      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29970      * @return {Boolean}
29971      */
29972     hasPanel : function(panel){
29973         if(typeof panel == "object"){ // must be panel obj
29974             panel = panel.getId();
29975         }
29976         return this.getPanel(panel) ? true : false;
29977     },
29978     
29979     /**
29980      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29981      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29982      * @param {Boolean} preservePanel Overrides the config preservePanel option
29983      * @return {Roo.ContentPanel} The panel that was removed
29984      */
29985     remove : function(panel, preservePanel){
29986         panel = this.getPanel(panel);
29987         if(!panel){
29988             return null;
29989         }
29990         var e = {};
29991         this.fireEvent("beforeremove", this, panel, e);
29992         if(e.cancel === true){
29993             return null;
29994         }
29995         var panelId = panel.getId();
29996         this.panels.removeKey(panelId);
29997         return panel;
29998     },
29999     
30000     /**
30001      * Returns the panel specified or null if it's not in this region.
30002      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30003      * @return {Roo.ContentPanel}
30004      */
30005     getPanel : function(id){
30006         if(typeof id == "object"){ // must be panel obj
30007             return id;
30008         }
30009         return this.panels.get(id);
30010     },
30011     
30012     /**
30013      * Returns this regions position (north/south/east/west/center).
30014      * @return {String} 
30015      */
30016     getPosition: function(){
30017         return this.position;    
30018     }
30019 });/*
30020  * Based on:
30021  * Ext JS Library 1.1.1
30022  * Copyright(c) 2006-2007, Ext JS, LLC.
30023  *
30024  * Originally Released Under LGPL - original licence link has changed is not relivant.
30025  *
30026  * Fork - LGPL
30027  * <script type="text/javascript">
30028  */
30029  
30030 /**
30031  * @class Roo.LayoutRegion
30032  * @extends Roo.BasicLayoutRegion
30033  * This class represents a region in a layout manager.
30034  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
30035  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
30036  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
30037  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
30038  * @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})
30039  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
30040  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
30041  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
30042  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
30043  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
30044  * @cfg {String}    title           The title for the region (overrides panel titles)
30045  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
30046  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
30047  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
30048  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
30049  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
30050  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
30051  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
30052  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
30053  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
30054  * @cfg {Boolean}   showPin         True to show a pin button
30055  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
30056  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
30057  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
30058  * @cfg {Number}    width           For East/West panels
30059  * @cfg {Number}    height          For North/South panels
30060  * @cfg {Boolean}   split           To show the splitter
30061  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
30062  */
30063 Roo.LayoutRegion = function(mgr, config, pos){
30064     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
30065     var dh = Roo.DomHelper;
30066     /** This region's container element 
30067     * @type Roo.Element */
30068     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
30069     /** This region's title element 
30070     * @type Roo.Element */
30071
30072     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
30073         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
30074         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
30075     ]}, true);
30076     this.titleEl.enableDisplayMode();
30077     /** This region's title text element 
30078     * @type HTMLElement */
30079     this.titleTextEl = this.titleEl.dom.firstChild;
30080     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
30081     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
30082     this.closeBtn.enableDisplayMode();
30083     this.closeBtn.on("click", this.closeClicked, this);
30084     this.closeBtn.hide();
30085
30086     this.createBody(config);
30087     this.visible = true;
30088     this.collapsed = false;
30089
30090     if(config.hideWhenEmpty){
30091         this.hide();
30092         this.on("paneladded", this.validateVisibility, this);
30093         this.on("panelremoved", this.validateVisibility, this);
30094     }
30095     this.applyConfig(config);
30096 };
30097
30098 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
30099
30100     createBody : function(){
30101         /** This region's body element 
30102         * @type Roo.Element */
30103         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30104     },
30105
30106     applyConfig : function(c){
30107         if(c.collapsible && this.position != "center" && !this.collapsedEl){
30108             var dh = Roo.DomHelper;
30109             if(c.titlebar !== false){
30110                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30111                 this.collapseBtn.on("click", this.collapse, this);
30112                 this.collapseBtn.enableDisplayMode();
30113
30114                 if(c.showPin === true || this.showPin){
30115                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30116                     this.stickBtn.enableDisplayMode();
30117                     this.stickBtn.on("click", this.expand, this);
30118                     this.stickBtn.hide();
30119                 }
30120             }
30121             /** This region's collapsed element
30122             * @type Roo.Element */
30123             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30124                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30125             ]}, true);
30126             if(c.floatable !== false){
30127                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30128                this.collapsedEl.on("click", this.collapseClick, this);
30129             }
30130
30131             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30132                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30133                    id: "message", unselectable: "on", style:{"float":"left"}});
30134                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30135              }
30136             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30137             this.expandBtn.on("click", this.expand, this);
30138         }
30139         if(this.collapseBtn){
30140             this.collapseBtn.setVisible(c.collapsible == true);
30141         }
30142         this.cmargins = c.cmargins || this.cmargins ||
30143                          (this.position == "west" || this.position == "east" ?
30144                              {top: 0, left: 2, right:2, bottom: 0} :
30145                              {top: 2, left: 0, right:0, bottom: 2});
30146         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30147         this.bottomTabs = c.tabPosition != "top";
30148         this.autoScroll = c.autoScroll || false;
30149         if(this.autoScroll){
30150             this.bodyEl.setStyle("overflow", "auto");
30151         }else{
30152             this.bodyEl.setStyle("overflow", "hidden");
30153         }
30154         //if(c.titlebar !== false){
30155             if((!c.titlebar && !c.title) || c.titlebar === false){
30156                 this.titleEl.hide();
30157             }else{
30158                 this.titleEl.show();
30159                 if(c.title){
30160                     this.titleTextEl.innerHTML = c.title;
30161                 }
30162             }
30163         //}
30164         this.duration = c.duration || .30;
30165         this.slideDuration = c.slideDuration || .45;
30166         this.config = c;
30167         if(c.collapsed){
30168             this.collapse(true);
30169         }
30170         if(c.hidden){
30171             this.hide();
30172         }
30173     },
30174     /**
30175      * Returns true if this region is currently visible.
30176      * @return {Boolean}
30177      */
30178     isVisible : function(){
30179         return this.visible;
30180     },
30181
30182     /**
30183      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30184      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
30185      */
30186     setCollapsedTitle : function(title){
30187         title = title || "&#160;";
30188         if(this.collapsedTitleTextEl){
30189             this.collapsedTitleTextEl.innerHTML = title;
30190         }
30191     },
30192
30193     getBox : function(){
30194         var b;
30195         if(!this.collapsed){
30196             b = this.el.getBox(false, true);
30197         }else{
30198             b = this.collapsedEl.getBox(false, true);
30199         }
30200         return b;
30201     },
30202
30203     getMargins : function(){
30204         return this.collapsed ? this.cmargins : this.margins;
30205     },
30206
30207     highlight : function(){
30208         this.el.addClass("x-layout-panel-dragover");
30209     },
30210
30211     unhighlight : function(){
30212         this.el.removeClass("x-layout-panel-dragover");
30213     },
30214
30215     updateBox : function(box){
30216         this.box = box;
30217         if(!this.collapsed){
30218             this.el.dom.style.left = box.x + "px";
30219             this.el.dom.style.top = box.y + "px";
30220             this.updateBody(box.width, box.height);
30221         }else{
30222             this.collapsedEl.dom.style.left = box.x + "px";
30223             this.collapsedEl.dom.style.top = box.y + "px";
30224             this.collapsedEl.setSize(box.width, box.height);
30225         }
30226         if(this.tabs){
30227             this.tabs.autoSizeTabs();
30228         }
30229     },
30230
30231     updateBody : function(w, h){
30232         if(w !== null){
30233             this.el.setWidth(w);
30234             w -= this.el.getBorderWidth("rl");
30235             if(this.config.adjustments){
30236                 w += this.config.adjustments[0];
30237             }
30238         }
30239         if(h !== null){
30240             this.el.setHeight(h);
30241             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
30242             h -= this.el.getBorderWidth("tb");
30243             if(this.config.adjustments){
30244                 h += this.config.adjustments[1];
30245             }
30246             this.bodyEl.setHeight(h);
30247             if(this.tabs){
30248                 h = this.tabs.syncHeight(h);
30249             }
30250         }
30251         if(this.panelSize){
30252             w = w !== null ? w : this.panelSize.width;
30253             h = h !== null ? h : this.panelSize.height;
30254         }
30255         if(this.activePanel){
30256             var el = this.activePanel.getEl();
30257             w = w !== null ? w : el.getWidth();
30258             h = h !== null ? h : el.getHeight();
30259             this.panelSize = {width: w, height: h};
30260             this.activePanel.setSize(w, h);
30261         }
30262         if(Roo.isIE && this.tabs){
30263             this.tabs.el.repaint();
30264         }
30265     },
30266
30267     /**
30268      * Returns the container element for this region.
30269      * @return {Roo.Element}
30270      */
30271     getEl : function(){
30272         return this.el;
30273     },
30274
30275     /**
30276      * Hides this region.
30277      */
30278     hide : function(){
30279         if(!this.collapsed){
30280             this.el.dom.style.left = "-2000px";
30281             this.el.hide();
30282         }else{
30283             this.collapsedEl.dom.style.left = "-2000px";
30284             this.collapsedEl.hide();
30285         }
30286         this.visible = false;
30287         this.fireEvent("visibilitychange", this, false);
30288     },
30289
30290     /**
30291      * Shows this region if it was previously hidden.
30292      */
30293     show : function(){
30294         if(!this.collapsed){
30295             this.el.show();
30296         }else{
30297             this.collapsedEl.show();
30298         }
30299         this.visible = true;
30300         this.fireEvent("visibilitychange", this, true);
30301     },
30302
30303     closeClicked : function(){
30304         if(this.activePanel){
30305             this.remove(this.activePanel);
30306         }
30307     },
30308
30309     collapseClick : function(e){
30310         if(this.isSlid){
30311            e.stopPropagation();
30312            this.slideIn();
30313         }else{
30314            e.stopPropagation();
30315            this.slideOut();
30316         }
30317     },
30318
30319     /**
30320      * Collapses this region.
30321      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30322      */
30323     collapse : function(skipAnim, skipCheck){
30324         if(this.collapsed) {
30325             return;
30326         }
30327         
30328         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
30329             
30330             this.collapsed = true;
30331             if(this.split){
30332                 this.split.el.hide();
30333             }
30334             if(this.config.animate && skipAnim !== true){
30335                 this.fireEvent("invalidated", this);
30336                 this.animateCollapse();
30337             }else{
30338                 this.el.setLocation(-20000,-20000);
30339                 this.el.hide();
30340                 this.collapsedEl.show();
30341                 this.fireEvent("collapsed", this);
30342                 this.fireEvent("invalidated", this);
30343             }
30344         }
30345         
30346     },
30347
30348     animateCollapse : function(){
30349         // overridden
30350     },
30351
30352     /**
30353      * Expands this region if it was previously collapsed.
30354      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30355      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30356      */
30357     expand : function(e, skipAnim){
30358         if(e) {
30359             e.stopPropagation();
30360         }
30361         if(!this.collapsed || this.el.hasActiveFx()) {
30362             return;
30363         }
30364         if(this.isSlid){
30365             this.afterSlideIn();
30366             skipAnim = true;
30367         }
30368         this.collapsed = false;
30369         if(this.config.animate && skipAnim !== true){
30370             this.animateExpand();
30371         }else{
30372             this.el.show();
30373             if(this.split){
30374                 this.split.el.show();
30375             }
30376             this.collapsedEl.setLocation(-2000,-2000);
30377             this.collapsedEl.hide();
30378             this.fireEvent("invalidated", this);
30379             this.fireEvent("expanded", this);
30380         }
30381     },
30382
30383     animateExpand : function(){
30384         // overridden
30385     },
30386
30387     initTabs : function()
30388     {
30389         this.bodyEl.setStyle("overflow", "hidden");
30390         var ts = new Roo.TabPanel(
30391                 this.bodyEl.dom,
30392                 {
30393                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
30394                     disableTooltips: this.config.disableTabTips,
30395                     toolbar : this.config.toolbar
30396                 }
30397         );
30398         if(this.config.hideTabs){
30399             ts.stripWrap.setDisplayed(false);
30400         }
30401         this.tabs = ts;
30402         ts.resizeTabs = this.config.resizeTabs === true;
30403         ts.minTabWidth = this.config.minTabWidth || 40;
30404         ts.maxTabWidth = this.config.maxTabWidth || 250;
30405         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30406         ts.monitorResize = false;
30407         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30408         ts.bodyEl.addClass('x-layout-tabs-body');
30409         this.panels.each(this.initPanelAsTab, this);
30410     },
30411
30412     initPanelAsTab : function(panel){
30413         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30414                     this.config.closeOnTab && panel.isClosable());
30415         if(panel.tabTip !== undefined){
30416             ti.setTooltip(panel.tabTip);
30417         }
30418         ti.on("activate", function(){
30419               this.setActivePanel(panel);
30420         }, this);
30421         if(this.config.closeOnTab){
30422             ti.on("beforeclose", function(t, e){
30423                 e.cancel = true;
30424                 this.remove(panel);
30425             }, this);
30426         }
30427         return ti;
30428     },
30429
30430     updatePanelTitle : function(panel, title){
30431         if(this.activePanel == panel){
30432             this.updateTitle(title);
30433         }
30434         if(this.tabs){
30435             var ti = this.tabs.getTab(panel.getEl().id);
30436             ti.setText(title);
30437             if(panel.tabTip !== undefined){
30438                 ti.setTooltip(panel.tabTip);
30439             }
30440         }
30441     },
30442
30443     updateTitle : function(title){
30444         if(this.titleTextEl && !this.config.title){
30445             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30446         }
30447     },
30448
30449     setActivePanel : function(panel){
30450         panel = this.getPanel(panel);
30451         if(this.activePanel && this.activePanel != panel){
30452             this.activePanel.setActiveState(false);
30453         }
30454         this.activePanel = panel;
30455         panel.setActiveState(true);
30456         if(this.panelSize){
30457             panel.setSize(this.panelSize.width, this.panelSize.height);
30458         }
30459         if(this.closeBtn){
30460             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30461         }
30462         this.updateTitle(panel.getTitle());
30463         if(this.tabs){
30464             this.fireEvent("invalidated", this);
30465         }
30466         this.fireEvent("panelactivated", this, panel);
30467     },
30468
30469     /**
30470      * Shows the specified panel.
30471      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30472      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30473      */
30474     showPanel : function(panel)
30475     {
30476         panel = this.getPanel(panel);
30477         if(panel){
30478             if(this.tabs){
30479                 var tab = this.tabs.getTab(panel.getEl().id);
30480                 if(tab.isHidden()){
30481                     this.tabs.unhideTab(tab.id);
30482                 }
30483                 tab.activate();
30484             }else{
30485                 this.setActivePanel(panel);
30486             }
30487         }
30488         return panel;
30489     },
30490
30491     /**
30492      * Get the active panel for this region.
30493      * @return {Roo.ContentPanel} The active panel or null
30494      */
30495     getActivePanel : function(){
30496         return this.activePanel;
30497     },
30498
30499     validateVisibility : function(){
30500         if(this.panels.getCount() < 1){
30501             this.updateTitle("&#160;");
30502             this.closeBtn.hide();
30503             this.hide();
30504         }else{
30505             if(!this.isVisible()){
30506                 this.show();
30507             }
30508         }
30509     },
30510
30511     /**
30512      * Adds the passed ContentPanel(s) to this region.
30513      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30514      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30515      */
30516     add : function(panel){
30517         if(arguments.length > 1){
30518             for(var i = 0, len = arguments.length; i < len; i++) {
30519                 this.add(arguments[i]);
30520             }
30521             return null;
30522         }
30523         if(this.hasPanel(panel)){
30524             this.showPanel(panel);
30525             return panel;
30526         }
30527         panel.setRegion(this);
30528         this.panels.add(panel);
30529         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30530             this.bodyEl.dom.appendChild(panel.getEl().dom);
30531             if(panel.background !== true){
30532                 this.setActivePanel(panel);
30533             }
30534             this.fireEvent("paneladded", this, panel);
30535             return panel;
30536         }
30537         if(!this.tabs){
30538             this.initTabs();
30539         }else{
30540             this.initPanelAsTab(panel);
30541         }
30542         if(panel.background !== true){
30543             this.tabs.activate(panel.getEl().id);
30544         }
30545         this.fireEvent("paneladded", this, panel);
30546         return panel;
30547     },
30548
30549     /**
30550      * Hides the tab for the specified panel.
30551      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30552      */
30553     hidePanel : function(panel){
30554         if(this.tabs && (panel = this.getPanel(panel))){
30555             this.tabs.hideTab(panel.getEl().id);
30556         }
30557     },
30558
30559     /**
30560      * Unhides the tab for a previously hidden panel.
30561      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30562      */
30563     unhidePanel : function(panel){
30564         if(this.tabs && (panel = this.getPanel(panel))){
30565             this.tabs.unhideTab(panel.getEl().id);
30566         }
30567     },
30568
30569     clearPanels : function(){
30570         while(this.panels.getCount() > 0){
30571              this.remove(this.panels.first());
30572         }
30573     },
30574
30575     /**
30576      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30577      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30578      * @param {Boolean} preservePanel Overrides the config preservePanel option
30579      * @return {Roo.ContentPanel} The panel that was removed
30580      */
30581     remove : function(panel, preservePanel){
30582         panel = this.getPanel(panel);
30583         if(!panel){
30584             return null;
30585         }
30586         var e = {};
30587         this.fireEvent("beforeremove", this, panel, e);
30588         if(e.cancel === true){
30589             return null;
30590         }
30591         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30592         var panelId = panel.getId();
30593         this.panels.removeKey(panelId);
30594         if(preservePanel){
30595             document.body.appendChild(panel.getEl().dom);
30596         }
30597         if(this.tabs){
30598             this.tabs.removeTab(panel.getEl().id);
30599         }else if (!preservePanel){
30600             this.bodyEl.dom.removeChild(panel.getEl().dom);
30601         }
30602         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30603             var p = this.panels.first();
30604             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30605             tempEl.appendChild(p.getEl().dom);
30606             this.bodyEl.update("");
30607             this.bodyEl.dom.appendChild(p.getEl().dom);
30608             tempEl = null;
30609             this.updateTitle(p.getTitle());
30610             this.tabs = null;
30611             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30612             this.setActivePanel(p);
30613         }
30614         panel.setRegion(null);
30615         if(this.activePanel == panel){
30616             this.activePanel = null;
30617         }
30618         if(this.config.autoDestroy !== false && preservePanel !== true){
30619             try{panel.destroy();}catch(e){}
30620         }
30621         this.fireEvent("panelremoved", this, panel);
30622         return panel;
30623     },
30624
30625     /**
30626      * Returns the TabPanel component used by this region
30627      * @return {Roo.TabPanel}
30628      */
30629     getTabs : function(){
30630         return this.tabs;
30631     },
30632
30633     createTool : function(parentEl, className){
30634         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30635             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30636         btn.addClassOnOver("x-layout-tools-button-over");
30637         return btn;
30638     }
30639 });/*
30640  * Based on:
30641  * Ext JS Library 1.1.1
30642  * Copyright(c) 2006-2007, Ext JS, LLC.
30643  *
30644  * Originally Released Under LGPL - original licence link has changed is not relivant.
30645  *
30646  * Fork - LGPL
30647  * <script type="text/javascript">
30648  */
30649  
30650
30651
30652 /**
30653  * @class Roo.SplitLayoutRegion
30654  * @extends Roo.LayoutRegion
30655  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30656  */
30657 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30658     this.cursor = cursor;
30659     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30660 };
30661
30662 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30663     splitTip : "Drag to resize.",
30664     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30665     useSplitTips : false,
30666
30667     applyConfig : function(config){
30668         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30669         if(config.split){
30670             if(!this.split){
30671                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30672                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30673                 /** The SplitBar for this region 
30674                 * @type Roo.SplitBar */
30675                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30676                 this.split.on("moved", this.onSplitMove, this);
30677                 this.split.useShim = config.useShim === true;
30678                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30679                 if(this.useSplitTips){
30680                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30681                 }
30682                 if(config.collapsible){
30683                     this.split.el.on("dblclick", this.collapse,  this);
30684                 }
30685             }
30686             if(typeof config.minSize != "undefined"){
30687                 this.split.minSize = config.minSize;
30688             }
30689             if(typeof config.maxSize != "undefined"){
30690                 this.split.maxSize = config.maxSize;
30691             }
30692             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30693                 this.hideSplitter();
30694             }
30695         }
30696     },
30697
30698     getHMaxSize : function(){
30699          var cmax = this.config.maxSize || 10000;
30700          var center = this.mgr.getRegion("center");
30701          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30702     },
30703
30704     getVMaxSize : function(){
30705          var cmax = this.config.maxSize || 10000;
30706          var center = this.mgr.getRegion("center");
30707          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30708     },
30709
30710     onSplitMove : function(split, newSize){
30711         this.fireEvent("resized", this, newSize);
30712     },
30713     
30714     /** 
30715      * Returns the {@link Roo.SplitBar} for this region.
30716      * @return {Roo.SplitBar}
30717      */
30718     getSplitBar : function(){
30719         return this.split;
30720     },
30721     
30722     hide : function(){
30723         this.hideSplitter();
30724         Roo.SplitLayoutRegion.superclass.hide.call(this);
30725     },
30726
30727     hideSplitter : function(){
30728         if(this.split){
30729             this.split.el.setLocation(-2000,-2000);
30730             this.split.el.hide();
30731         }
30732     },
30733
30734     show : function(){
30735         if(this.split){
30736             this.split.el.show();
30737         }
30738         Roo.SplitLayoutRegion.superclass.show.call(this);
30739     },
30740     
30741     beforeSlide: function(){
30742         if(Roo.isGecko){// firefox overflow auto bug workaround
30743             this.bodyEl.clip();
30744             if(this.tabs) {
30745                 this.tabs.bodyEl.clip();
30746             }
30747             if(this.activePanel){
30748                 this.activePanel.getEl().clip();
30749                 
30750                 if(this.activePanel.beforeSlide){
30751                     this.activePanel.beforeSlide();
30752                 }
30753             }
30754         }
30755     },
30756     
30757     afterSlide : function(){
30758         if(Roo.isGecko){// firefox overflow auto bug workaround
30759             this.bodyEl.unclip();
30760             if(this.tabs) {
30761                 this.tabs.bodyEl.unclip();
30762             }
30763             if(this.activePanel){
30764                 this.activePanel.getEl().unclip();
30765                 if(this.activePanel.afterSlide){
30766                     this.activePanel.afterSlide();
30767                 }
30768             }
30769         }
30770     },
30771
30772     initAutoHide : function(){
30773         if(this.autoHide !== false){
30774             if(!this.autoHideHd){
30775                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30776                 this.autoHideHd = {
30777                     "mouseout": function(e){
30778                         if(!e.within(this.el, true)){
30779                             st.delay(500);
30780                         }
30781                     },
30782                     "mouseover" : function(e){
30783                         st.cancel();
30784                     },
30785                     scope : this
30786                 };
30787             }
30788             this.el.on(this.autoHideHd);
30789         }
30790     },
30791
30792     clearAutoHide : function(){
30793         if(this.autoHide !== false){
30794             this.el.un("mouseout", this.autoHideHd.mouseout);
30795             this.el.un("mouseover", this.autoHideHd.mouseover);
30796         }
30797     },
30798
30799     clearMonitor : function(){
30800         Roo.get(document).un("click", this.slideInIf, this);
30801     },
30802
30803     // these names are backwards but not changed for compat
30804     slideOut : function(){
30805         if(this.isSlid || this.el.hasActiveFx()){
30806             return;
30807         }
30808         this.isSlid = true;
30809         if(this.collapseBtn){
30810             this.collapseBtn.hide();
30811         }
30812         this.closeBtnState = this.closeBtn.getStyle('display');
30813         this.closeBtn.hide();
30814         if(this.stickBtn){
30815             this.stickBtn.show();
30816         }
30817         this.el.show();
30818         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30819         this.beforeSlide();
30820         this.el.setStyle("z-index", 10001);
30821         this.el.slideIn(this.getSlideAnchor(), {
30822             callback: function(){
30823                 this.afterSlide();
30824                 this.initAutoHide();
30825                 Roo.get(document).on("click", this.slideInIf, this);
30826                 this.fireEvent("slideshow", this);
30827             },
30828             scope: this,
30829             block: true
30830         });
30831     },
30832
30833     afterSlideIn : function(){
30834         this.clearAutoHide();
30835         this.isSlid = false;
30836         this.clearMonitor();
30837         this.el.setStyle("z-index", "");
30838         if(this.collapseBtn){
30839             this.collapseBtn.show();
30840         }
30841         this.closeBtn.setStyle('display', this.closeBtnState);
30842         if(this.stickBtn){
30843             this.stickBtn.hide();
30844         }
30845         this.fireEvent("slidehide", this);
30846     },
30847
30848     slideIn : function(cb){
30849         if(!this.isSlid || this.el.hasActiveFx()){
30850             Roo.callback(cb);
30851             return;
30852         }
30853         this.isSlid = false;
30854         this.beforeSlide();
30855         this.el.slideOut(this.getSlideAnchor(), {
30856             callback: function(){
30857                 this.el.setLeftTop(-10000, -10000);
30858                 this.afterSlide();
30859                 this.afterSlideIn();
30860                 Roo.callback(cb);
30861             },
30862             scope: this,
30863             block: true
30864         });
30865     },
30866     
30867     slideInIf : function(e){
30868         if(!e.within(this.el)){
30869             this.slideIn();
30870         }
30871     },
30872
30873     animateCollapse : function(){
30874         this.beforeSlide();
30875         this.el.setStyle("z-index", 20000);
30876         var anchor = this.getSlideAnchor();
30877         this.el.slideOut(anchor, {
30878             callback : function(){
30879                 this.el.setStyle("z-index", "");
30880                 this.collapsedEl.slideIn(anchor, {duration:.3});
30881                 this.afterSlide();
30882                 this.el.setLocation(-10000,-10000);
30883                 this.el.hide();
30884                 this.fireEvent("collapsed", this);
30885             },
30886             scope: this,
30887             block: true
30888         });
30889     },
30890
30891     animateExpand : function(){
30892         this.beforeSlide();
30893         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30894         this.el.setStyle("z-index", 20000);
30895         this.collapsedEl.hide({
30896             duration:.1
30897         });
30898         this.el.slideIn(this.getSlideAnchor(), {
30899             callback : function(){
30900                 this.el.setStyle("z-index", "");
30901                 this.afterSlide();
30902                 if(this.split){
30903                     this.split.el.show();
30904                 }
30905                 this.fireEvent("invalidated", this);
30906                 this.fireEvent("expanded", this);
30907             },
30908             scope: this,
30909             block: true
30910         });
30911     },
30912
30913     anchors : {
30914         "west" : "left",
30915         "east" : "right",
30916         "north" : "top",
30917         "south" : "bottom"
30918     },
30919
30920     sanchors : {
30921         "west" : "l",
30922         "east" : "r",
30923         "north" : "t",
30924         "south" : "b"
30925     },
30926
30927     canchors : {
30928         "west" : "tl-tr",
30929         "east" : "tr-tl",
30930         "north" : "tl-bl",
30931         "south" : "bl-tl"
30932     },
30933
30934     getAnchor : function(){
30935         return this.anchors[this.position];
30936     },
30937
30938     getCollapseAnchor : function(){
30939         return this.canchors[this.position];
30940     },
30941
30942     getSlideAnchor : function(){
30943         return this.sanchors[this.position];
30944     },
30945
30946     getAlignAdj : function(){
30947         var cm = this.cmargins;
30948         switch(this.position){
30949             case "west":
30950                 return [0, 0];
30951             break;
30952             case "east":
30953                 return [0, 0];
30954             break;
30955             case "north":
30956                 return [0, 0];
30957             break;
30958             case "south":
30959                 return [0, 0];
30960             break;
30961         }
30962     },
30963
30964     getExpandAdj : function(){
30965         var c = this.collapsedEl, cm = this.cmargins;
30966         switch(this.position){
30967             case "west":
30968                 return [-(cm.right+c.getWidth()+cm.left), 0];
30969             break;
30970             case "east":
30971                 return [cm.right+c.getWidth()+cm.left, 0];
30972             break;
30973             case "north":
30974                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30975             break;
30976             case "south":
30977                 return [0, cm.top+cm.bottom+c.getHeight()];
30978             break;
30979         }
30980     }
30981 });/*
30982  * Based on:
30983  * Ext JS Library 1.1.1
30984  * Copyright(c) 2006-2007, Ext JS, LLC.
30985  *
30986  * Originally Released Under LGPL - original licence link has changed is not relivant.
30987  *
30988  * Fork - LGPL
30989  * <script type="text/javascript">
30990  */
30991 /*
30992  * These classes are private internal classes
30993  */
30994 Roo.CenterLayoutRegion = function(mgr, config){
30995     Roo.LayoutRegion.call(this, mgr, config, "center");
30996     this.visible = true;
30997     this.minWidth = config.minWidth || 20;
30998     this.minHeight = config.minHeight || 20;
30999 };
31000
31001 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
31002     hide : function(){
31003         // center panel can't be hidden
31004     },
31005     
31006     show : function(){
31007         // center panel can't be hidden
31008     },
31009     
31010     getMinWidth: function(){
31011         return this.minWidth;
31012     },
31013     
31014     getMinHeight: function(){
31015         return this.minHeight;
31016     }
31017 });
31018
31019
31020 Roo.NorthLayoutRegion = function(mgr, config){
31021     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
31022     if(this.split){
31023         this.split.placement = Roo.SplitBar.TOP;
31024         this.split.orientation = Roo.SplitBar.VERTICAL;
31025         this.split.el.addClass("x-layout-split-v");
31026     }
31027     var size = config.initialSize || config.height;
31028     if(typeof size != "undefined"){
31029         this.el.setHeight(size);
31030     }
31031 };
31032 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
31033     orientation: Roo.SplitBar.VERTICAL,
31034     getBox : function(){
31035         if(this.collapsed){
31036             return this.collapsedEl.getBox();
31037         }
31038         var box = this.el.getBox();
31039         if(this.split){
31040             box.height += this.split.el.getHeight();
31041         }
31042         return box;
31043     },
31044     
31045     updateBox : function(box){
31046         if(this.split && !this.collapsed){
31047             box.height -= this.split.el.getHeight();
31048             this.split.el.setLeft(box.x);
31049             this.split.el.setTop(box.y+box.height);
31050             this.split.el.setWidth(box.width);
31051         }
31052         if(this.collapsed){
31053             this.updateBody(box.width, null);
31054         }
31055         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31056     }
31057 });
31058
31059 Roo.SouthLayoutRegion = function(mgr, config){
31060     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
31061     if(this.split){
31062         this.split.placement = Roo.SplitBar.BOTTOM;
31063         this.split.orientation = Roo.SplitBar.VERTICAL;
31064         this.split.el.addClass("x-layout-split-v");
31065     }
31066     var size = config.initialSize || config.height;
31067     if(typeof size != "undefined"){
31068         this.el.setHeight(size);
31069     }
31070 };
31071 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
31072     orientation: Roo.SplitBar.VERTICAL,
31073     getBox : function(){
31074         if(this.collapsed){
31075             return this.collapsedEl.getBox();
31076         }
31077         var box = this.el.getBox();
31078         if(this.split){
31079             var sh = this.split.el.getHeight();
31080             box.height += sh;
31081             box.y -= sh;
31082         }
31083         return box;
31084     },
31085     
31086     updateBox : function(box){
31087         if(this.split && !this.collapsed){
31088             var sh = this.split.el.getHeight();
31089             box.height -= sh;
31090             box.y += sh;
31091             this.split.el.setLeft(box.x);
31092             this.split.el.setTop(box.y-sh);
31093             this.split.el.setWidth(box.width);
31094         }
31095         if(this.collapsed){
31096             this.updateBody(box.width, null);
31097         }
31098         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31099     }
31100 });
31101
31102 Roo.EastLayoutRegion = function(mgr, config){
31103     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31104     if(this.split){
31105         this.split.placement = Roo.SplitBar.RIGHT;
31106         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31107         this.split.el.addClass("x-layout-split-h");
31108     }
31109     var size = config.initialSize || config.width;
31110     if(typeof size != "undefined"){
31111         this.el.setWidth(size);
31112     }
31113 };
31114 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31115     orientation: Roo.SplitBar.HORIZONTAL,
31116     getBox : function(){
31117         if(this.collapsed){
31118             return this.collapsedEl.getBox();
31119         }
31120         var box = this.el.getBox();
31121         if(this.split){
31122             var sw = this.split.el.getWidth();
31123             box.width += sw;
31124             box.x -= sw;
31125         }
31126         return box;
31127     },
31128
31129     updateBox : function(box){
31130         if(this.split && !this.collapsed){
31131             var sw = this.split.el.getWidth();
31132             box.width -= sw;
31133             this.split.el.setLeft(box.x);
31134             this.split.el.setTop(box.y);
31135             this.split.el.setHeight(box.height);
31136             box.x += sw;
31137         }
31138         if(this.collapsed){
31139             this.updateBody(null, box.height);
31140         }
31141         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31142     }
31143 });
31144
31145 Roo.WestLayoutRegion = function(mgr, config){
31146     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31147     if(this.split){
31148         this.split.placement = Roo.SplitBar.LEFT;
31149         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31150         this.split.el.addClass("x-layout-split-h");
31151     }
31152     var size = config.initialSize || config.width;
31153     if(typeof size != "undefined"){
31154         this.el.setWidth(size);
31155     }
31156 };
31157 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31158     orientation: Roo.SplitBar.HORIZONTAL,
31159     getBox : function(){
31160         if(this.collapsed){
31161             return this.collapsedEl.getBox();
31162         }
31163         var box = this.el.getBox();
31164         if(this.split){
31165             box.width += this.split.el.getWidth();
31166         }
31167         return box;
31168     },
31169     
31170     updateBox : function(box){
31171         if(this.split && !this.collapsed){
31172             var sw = this.split.el.getWidth();
31173             box.width -= sw;
31174             this.split.el.setLeft(box.x+box.width);
31175             this.split.el.setTop(box.y);
31176             this.split.el.setHeight(box.height);
31177         }
31178         if(this.collapsed){
31179             this.updateBody(null, box.height);
31180         }
31181         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31182     }
31183 });
31184 /*
31185  * Based on:
31186  * Ext JS Library 1.1.1
31187  * Copyright(c) 2006-2007, Ext JS, LLC.
31188  *
31189  * Originally Released Under LGPL - original licence link has changed is not relivant.
31190  *
31191  * Fork - LGPL
31192  * <script type="text/javascript">
31193  */
31194  
31195  
31196 /*
31197  * Private internal class for reading and applying state
31198  */
31199 Roo.LayoutStateManager = function(layout){
31200      // default empty state
31201      this.state = {
31202         north: {},
31203         south: {},
31204         east: {},
31205         west: {}       
31206     };
31207 };
31208
31209 Roo.LayoutStateManager.prototype = {
31210     init : function(layout, provider){
31211         this.provider = provider;
31212         var state = provider.get(layout.id+"-layout-state");
31213         if(state){
31214             var wasUpdating = layout.isUpdating();
31215             if(!wasUpdating){
31216                 layout.beginUpdate();
31217             }
31218             for(var key in state){
31219                 if(typeof state[key] != "function"){
31220                     var rstate = state[key];
31221                     var r = layout.getRegion(key);
31222                     if(r && rstate){
31223                         if(rstate.size){
31224                             r.resizeTo(rstate.size);
31225                         }
31226                         if(rstate.collapsed == true){
31227                             r.collapse(true);
31228                         }else{
31229                             r.expand(null, true);
31230                         }
31231                     }
31232                 }
31233             }
31234             if(!wasUpdating){
31235                 layout.endUpdate();
31236             }
31237             this.state = state; 
31238         }
31239         this.layout = layout;
31240         layout.on("regionresized", this.onRegionResized, this);
31241         layout.on("regioncollapsed", this.onRegionCollapsed, this);
31242         layout.on("regionexpanded", this.onRegionExpanded, this);
31243     },
31244     
31245     storeState : function(){
31246         this.provider.set(this.layout.id+"-layout-state", this.state);
31247     },
31248     
31249     onRegionResized : function(region, newSize){
31250         this.state[region.getPosition()].size = newSize;
31251         this.storeState();
31252     },
31253     
31254     onRegionCollapsed : function(region){
31255         this.state[region.getPosition()].collapsed = true;
31256         this.storeState();
31257     },
31258     
31259     onRegionExpanded : function(region){
31260         this.state[region.getPosition()].collapsed = false;
31261         this.storeState();
31262     }
31263 };/*
31264  * Based on:
31265  * Ext JS Library 1.1.1
31266  * Copyright(c) 2006-2007, Ext JS, LLC.
31267  *
31268  * Originally Released Under LGPL - original licence link has changed is not relivant.
31269  *
31270  * Fork - LGPL
31271  * <script type="text/javascript">
31272  */
31273 /**
31274  * @class Roo.ContentPanel
31275  * @extends Roo.util.Observable
31276  * @children Roo.form.Form Roo.JsonView Roo.View
31277  * @parent Roo.BorderLayout Roo.LayoutDialog builder-top
31278  * A basic ContentPanel element.
31279  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
31280  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
31281  * @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
31282  * @cfg {Boolean}   closable      True if the panel can be closed/removed
31283  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
31284  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
31285  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
31286  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
31287  * @cfg {String} title          The title for this panel
31288  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
31289  * @cfg {String} url            Calls {@link #setUrl} with this value
31290  * @cfg {String} region [required]   (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
31291  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
31292  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
31293  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
31294  * @cfg {String}    style  Extra style to add to the content panel
31295  * @cfg {Roo.menu.Menu} menu  popup menu
31296
31297  * @constructor
31298  * Create a new ContentPanel.
31299  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
31300  * @param {String/Object} config A string to set only the title or a config object
31301  * @param {String} content (optional) Set the HTML content for this panel
31302  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
31303  */
31304 Roo.ContentPanel = function(el, config, content){
31305     
31306      
31307     /*
31308     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
31309         config = el;
31310         el = Roo.id();
31311     }
31312     if (config && config.parentLayout) { 
31313         el = config.parentLayout.el.createChild(); 
31314     }
31315     */
31316     if(el.autoCreate){ // xtype is available if this is called from factory
31317         config = el;
31318         el = Roo.id();
31319     }
31320     this.el = Roo.get(el);
31321     if(!this.el && config && config.autoCreate){
31322         if(typeof config.autoCreate == "object"){
31323             if(!config.autoCreate.id){
31324                 config.autoCreate.id = config.id||el;
31325             }
31326             this.el = Roo.DomHelper.append(document.body,
31327                         config.autoCreate, true);
31328         }else{
31329             this.el = Roo.DomHelper.append(document.body,
31330                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
31331         }
31332     }
31333     
31334     
31335     this.closable = false;
31336     this.loaded = false;
31337     this.active = false;
31338     if(typeof config == "string"){
31339         this.title = config;
31340     }else{
31341         Roo.apply(this, config);
31342     }
31343     
31344     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31345         this.wrapEl = this.el.wrap();
31346         this.toolbar.container = this.el.insertSibling(false, 'before');
31347         this.toolbar = new Roo.Toolbar(this.toolbar);
31348     }
31349     
31350     // xtype created footer. - not sure if will work as we normally have to render first..
31351     if (this.footer && !this.footer.el && this.footer.xtype) {
31352         if (!this.wrapEl) {
31353             this.wrapEl = this.el.wrap();
31354         }
31355     
31356         this.footer.container = this.wrapEl.createChild();
31357          
31358         this.footer = Roo.factory(this.footer, Roo);
31359         
31360     }
31361     
31362     if(this.resizeEl){
31363         this.resizeEl = Roo.get(this.resizeEl, true);
31364     }else{
31365         this.resizeEl = this.el;
31366     }
31367     // handle view.xtype
31368     
31369  
31370     
31371     
31372     this.addEvents({
31373         /**
31374          * @event activate
31375          * Fires when this panel is activated. 
31376          * @param {Roo.ContentPanel} this
31377          */
31378         "activate" : true,
31379         /**
31380          * @event deactivate
31381          * Fires when this panel is activated. 
31382          * @param {Roo.ContentPanel} this
31383          */
31384         "deactivate" : true,
31385
31386         /**
31387          * @event resize
31388          * Fires when this panel is resized if fitToFrame is true.
31389          * @param {Roo.ContentPanel} this
31390          * @param {Number} width The width after any component adjustments
31391          * @param {Number} height The height after any component adjustments
31392          */
31393         "resize" : true,
31394         
31395          /**
31396          * @event render
31397          * Fires when this tab is created
31398          * @param {Roo.ContentPanel} this
31399          */
31400         "render" : true
31401          
31402         
31403     });
31404     
31405
31406     
31407     
31408     if(this.autoScroll){
31409         this.resizeEl.setStyle("overflow", "auto");
31410     } else {
31411         // fix randome scrolling
31412         this.el.on('scroll', function() {
31413             Roo.log('fix random scolling');
31414             this.scrollTo('top',0); 
31415         });
31416     }
31417     content = content || this.content;
31418     if(content){
31419         this.setContent(content);
31420     }
31421     if(config && config.url){
31422         this.setUrl(this.url, this.params, this.loadOnce);
31423     }
31424     
31425     
31426     
31427     Roo.ContentPanel.superclass.constructor.call(this);
31428     
31429     if (this.view && typeof(this.view.xtype) != 'undefined') {
31430         this.view.el = this.el.appendChild(document.createElement("div"));
31431         this.view = Roo.factory(this.view); 
31432         this.view.render  &&  this.view.render(false, '');  
31433     }
31434     
31435     
31436     this.fireEvent('render', this);
31437 };
31438
31439 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31440     tabTip:'',
31441     setRegion : function(region){
31442         this.region = region;
31443         if(region){
31444            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31445         }else{
31446            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31447         } 
31448     },
31449     
31450     /**
31451      * Returns the toolbar for this Panel if one was configured. 
31452      * @return {Roo.Toolbar} 
31453      */
31454     getToolbar : function(){
31455         return this.toolbar;
31456     },
31457     
31458     setActiveState : function(active){
31459         this.active = active;
31460         if(!active){
31461             this.fireEvent("deactivate", this);
31462         }else{
31463             this.fireEvent("activate", this);
31464         }
31465     },
31466     /**
31467      * Updates this panel's element
31468      * @param {String} content The new content
31469      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31470     */
31471     setContent : function(content, loadScripts){
31472         this.el.update(content, loadScripts);
31473     },
31474
31475     ignoreResize : function(w, h){
31476         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31477             return true;
31478         }else{
31479             this.lastSize = {width: w, height: h};
31480             return false;
31481         }
31482     },
31483     /**
31484      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31485      * @return {Roo.UpdateManager} The UpdateManager
31486      */
31487     getUpdateManager : function(){
31488         return this.el.getUpdateManager();
31489     },
31490      /**
31491      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31492      * @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:
31493 <pre><code>
31494 panel.load({
31495     url: "your-url.php",
31496     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31497     callback: yourFunction,
31498     scope: yourObject, //(optional scope)
31499     discardUrl: false,
31500     nocache: false,
31501     text: "Loading...",
31502     timeout: 30,
31503     scripts: false
31504 });
31505 </code></pre>
31506      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31507      * 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.
31508      * @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}
31509      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31510      * @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.
31511      * @return {Roo.ContentPanel} this
31512      */
31513     load : function(){
31514         var um = this.el.getUpdateManager();
31515         um.update.apply(um, arguments);
31516         return this;
31517     },
31518
31519
31520     /**
31521      * 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.
31522      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31523      * @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)
31524      * @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)
31525      * @return {Roo.UpdateManager} The UpdateManager
31526      */
31527     setUrl : function(url, params, loadOnce){
31528         if(this.refreshDelegate){
31529             this.removeListener("activate", this.refreshDelegate);
31530         }
31531         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31532         this.on("activate", this.refreshDelegate);
31533         return this.el.getUpdateManager();
31534     },
31535     
31536     _handleRefresh : function(url, params, loadOnce){
31537         if(!loadOnce || !this.loaded){
31538             var updater = this.el.getUpdateManager();
31539             updater.update(url, params, this._setLoaded.createDelegate(this));
31540         }
31541     },
31542     
31543     _setLoaded : function(){
31544         this.loaded = true;
31545     }, 
31546     
31547     /**
31548      * Returns this panel's id
31549      * @return {String} 
31550      */
31551     getId : function(){
31552         return this.el.id;
31553     },
31554     
31555     /** 
31556      * Returns this panel's element - used by regiosn to add.
31557      * @return {Roo.Element} 
31558      */
31559     getEl : function(){
31560         return this.wrapEl || this.el;
31561     },
31562     
31563     adjustForComponents : function(width, height)
31564     {
31565         //Roo.log('adjustForComponents ');
31566         if(this.resizeEl != this.el){
31567             width -= this.el.getFrameWidth('lr');
31568             height -= this.el.getFrameWidth('tb');
31569         }
31570         if(this.toolbar){
31571             var te = this.toolbar.getEl();
31572             height -= te.getHeight();
31573             te.setWidth(width);
31574         }
31575         if(this.footer){
31576             var te = this.footer.getEl();
31577             //Roo.log("footer:" + te.getHeight());
31578             
31579             height -= te.getHeight();
31580             te.setWidth(width);
31581         }
31582         
31583         
31584         if(this.adjustments){
31585             width += this.adjustments[0];
31586             height += this.adjustments[1];
31587         }
31588         return {"width": width, "height": height};
31589     },
31590     
31591     setSize : function(width, height){
31592         if(this.fitToFrame && !this.ignoreResize(width, height)){
31593             if(this.fitContainer && this.resizeEl != this.el){
31594                 this.el.setSize(width, height);
31595             }
31596             var size = this.adjustForComponents(width, height);
31597             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31598             this.fireEvent('resize', this, size.width, size.height);
31599         }
31600     },
31601     
31602     /**
31603      * Returns this panel's title
31604      * @return {String} 
31605      */
31606     getTitle : function(){
31607         return this.title;
31608     },
31609     
31610     /**
31611      * Set this panel's title
31612      * @param {String} title
31613      */
31614     setTitle : function(title){
31615         this.title = title;
31616         if(this.region){
31617             this.region.updatePanelTitle(this, title);
31618         }
31619     },
31620     
31621     /**
31622      * Returns true is this panel was configured to be closable
31623      * @return {Boolean} 
31624      */
31625     isClosable : function(){
31626         return this.closable;
31627     },
31628     
31629     beforeSlide : function(){
31630         this.el.clip();
31631         this.resizeEl.clip();
31632     },
31633     
31634     afterSlide : function(){
31635         this.el.unclip();
31636         this.resizeEl.unclip();
31637     },
31638     
31639     /**
31640      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31641      *   Will fail silently if the {@link #setUrl} method has not been called.
31642      *   This does not activate the panel, just updates its content.
31643      */
31644     refresh : function(){
31645         if(this.refreshDelegate){
31646            this.loaded = false;
31647            this.refreshDelegate();
31648         }
31649     },
31650     
31651     /**
31652      * Destroys this panel
31653      */
31654     destroy : function(){
31655         this.el.removeAllListeners();
31656         var tempEl = document.createElement("span");
31657         tempEl.appendChild(this.el.dom);
31658         tempEl.innerHTML = "";
31659         this.el.remove();
31660         this.el = null;
31661     },
31662     
31663     /**
31664      * form - if the content panel contains a form - this is a reference to it.
31665      * @type {Roo.form.Form}
31666      */
31667     form : false,
31668     /**
31669      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31670      *    This contains a reference to it.
31671      * @type {Roo.View}
31672      */
31673     view : false,
31674     
31675       /**
31676      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31677      * <pre><code>
31678
31679 layout.addxtype({
31680        xtype : 'Form',
31681        items: [ .... ]
31682    }
31683 );
31684
31685 </code></pre>
31686      * @param {Object} cfg Xtype definition of item to add.
31687      */
31688     
31689     addxtype : function(cfg) {
31690         // add form..
31691         if (cfg.xtype.match(/^Form$/)) {
31692             
31693             var el;
31694             //if (this.footer) {
31695             //    el = this.footer.container.insertSibling(false, 'before');
31696             //} else {
31697                 el = this.el.createChild();
31698             //}
31699
31700             this.form = new  Roo.form.Form(cfg);
31701             
31702             
31703             if ( this.form.allItems.length) {
31704                 this.form.render(el.dom);
31705             }
31706             return this.form;
31707         }
31708         // should only have one of theses..
31709         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31710             // views.. should not be just added - used named prop 'view''
31711             
31712             cfg.el = this.el.appendChild(document.createElement("div"));
31713             // factory?
31714             
31715             var ret = new Roo.factory(cfg);
31716              
31717              ret.render && ret.render(false, ''); // render blank..
31718             this.view = ret;
31719             return ret;
31720         }
31721         return false;
31722     }
31723 });
31724
31725 /**
31726  * @class Roo.GridPanel
31727  * @extends Roo.ContentPanel
31728  * @constructor
31729  * Create a new GridPanel.
31730  * @param {Roo.grid.Grid} grid The grid for this panel
31731  * @param {String/Object} config A string to set only the panel's title, or a config object
31732  */
31733 Roo.GridPanel = function(grid, config){
31734     
31735   
31736     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31737         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31738         
31739     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31740     
31741     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31742     
31743     if(this.toolbar){
31744         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31745     }
31746     // xtype created footer. - not sure if will work as we normally have to render first..
31747     if (this.footer && !this.footer.el && this.footer.xtype) {
31748         
31749         this.footer.container = this.grid.getView().getFooterPanel(true);
31750         this.footer.dataSource = this.grid.dataSource;
31751         this.footer = Roo.factory(this.footer, Roo);
31752         
31753     }
31754     
31755     grid.monitorWindowResize = false; // turn off autosizing
31756     grid.autoHeight = false;
31757     grid.autoWidth = false;
31758     this.grid = grid;
31759     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31760 };
31761
31762 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31763     getId : function(){
31764         return this.grid.id;
31765     },
31766     
31767     /**
31768      * Returns the grid for this panel
31769      * @return {Roo.grid.Grid} 
31770      */
31771     getGrid : function(){
31772         return this.grid;    
31773     },
31774     
31775     setSize : function(width, height){
31776         if(!this.ignoreResize(width, height)){
31777             var grid = this.grid;
31778             var size = this.adjustForComponents(width, height);
31779             grid.getGridEl().setSize(size.width, size.height);
31780             grid.autoSize();
31781         }
31782     },
31783     
31784     beforeSlide : function(){
31785         this.grid.getView().scroller.clip();
31786     },
31787     
31788     afterSlide : function(){
31789         this.grid.getView().scroller.unclip();
31790     },
31791     
31792     destroy : function(){
31793         this.grid.destroy();
31794         delete this.grid;
31795         Roo.GridPanel.superclass.destroy.call(this); 
31796     }
31797 });
31798
31799
31800 /**
31801  * @class Roo.NestedLayoutPanel
31802  * @extends Roo.ContentPanel
31803  * @constructor
31804  * Create a new NestedLayoutPanel.
31805  * 
31806  * 
31807  * @param {Roo.BorderLayout} layout [required] The layout for this panel
31808  * @param {String/Object} config A string to set only the title or a config object
31809  */
31810 Roo.NestedLayoutPanel = function(layout, config)
31811 {
31812     // construct with only one argument..
31813     /* FIXME - implement nicer consturctors
31814     if (layout.layout) {
31815         config = layout;
31816         layout = config.layout;
31817         delete config.layout;
31818     }
31819     if (layout.xtype && !layout.getEl) {
31820         // then layout needs constructing..
31821         layout = Roo.factory(layout, Roo);
31822     }
31823     */
31824     
31825     
31826     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31827     
31828     layout.monitorWindowResize = false; // turn off autosizing
31829     this.layout = layout;
31830     this.layout.getEl().addClass("x-layout-nested-layout");
31831     
31832     
31833     
31834     
31835 };
31836
31837 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31838
31839     setSize : function(width, height){
31840         if(!this.ignoreResize(width, height)){
31841             var size = this.adjustForComponents(width, height);
31842             var el = this.layout.getEl();
31843             el.setSize(size.width, size.height);
31844             var touch = el.dom.offsetWidth;
31845             this.layout.layout();
31846             // ie requires a double layout on the first pass
31847             if(Roo.isIE && !this.initialized){
31848                 this.initialized = true;
31849                 this.layout.layout();
31850             }
31851         }
31852     },
31853     
31854     // activate all subpanels if not currently active..
31855     
31856     setActiveState : function(active){
31857         this.active = active;
31858         if(!active){
31859             this.fireEvent("deactivate", this);
31860             return;
31861         }
31862         
31863         this.fireEvent("activate", this);
31864         // not sure if this should happen before or after..
31865         if (!this.layout) {
31866             return; // should not happen..
31867         }
31868         var reg = false;
31869         for (var r in this.layout.regions) {
31870             reg = this.layout.getRegion(r);
31871             if (reg.getActivePanel()) {
31872                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31873                 reg.setActivePanel(reg.getActivePanel());
31874                 continue;
31875             }
31876             if (!reg.panels.length) {
31877                 continue;
31878             }
31879             reg.showPanel(reg.getPanel(0));
31880         }
31881         
31882         
31883         
31884         
31885     },
31886     
31887     /**
31888      * Returns the nested BorderLayout for this panel
31889      * @return {Roo.BorderLayout} 
31890      */
31891     getLayout : function(){
31892         return this.layout;
31893     },
31894     
31895      /**
31896      * Adds a xtype elements to the layout of the nested panel
31897      * <pre><code>
31898
31899 panel.addxtype({
31900        xtype : 'ContentPanel',
31901        region: 'west',
31902        items: [ .... ]
31903    }
31904 );
31905
31906 panel.addxtype({
31907         xtype : 'NestedLayoutPanel',
31908         region: 'west',
31909         layout: {
31910            center: { },
31911            west: { }   
31912         },
31913         items : [ ... list of content panels or nested layout panels.. ]
31914    }
31915 );
31916 </code></pre>
31917      * @param {Object} cfg Xtype definition of item to add.
31918      */
31919     addxtype : function(cfg) {
31920         return this.layout.addxtype(cfg);
31921     
31922     }
31923 });
31924
31925 Roo.ScrollPanel = function(el, config, content){
31926     config = config || {};
31927     config.fitToFrame = true;
31928     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31929     
31930     this.el.dom.style.overflow = "hidden";
31931     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31932     this.el.removeClass("x-layout-inactive-content");
31933     this.el.on("mousewheel", this.onWheel, this);
31934
31935     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31936     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31937     up.unselectable(); down.unselectable();
31938     up.on("click", this.scrollUp, this);
31939     down.on("click", this.scrollDown, this);
31940     up.addClassOnOver("x-scroller-btn-over");
31941     down.addClassOnOver("x-scroller-btn-over");
31942     up.addClassOnClick("x-scroller-btn-click");
31943     down.addClassOnClick("x-scroller-btn-click");
31944     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31945
31946     this.resizeEl = this.el;
31947     this.el = wrap; this.up = up; this.down = down;
31948 };
31949
31950 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31951     increment : 100,
31952     wheelIncrement : 5,
31953     scrollUp : function(){
31954         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31955     },
31956
31957     scrollDown : function(){
31958         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31959     },
31960
31961     afterScroll : function(){
31962         var el = this.resizeEl;
31963         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31964         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31965         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31966     },
31967
31968     setSize : function(){
31969         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31970         this.afterScroll();
31971     },
31972
31973     onWheel : function(e){
31974         var d = e.getWheelDelta();
31975         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31976         this.afterScroll();
31977         e.stopEvent();
31978     },
31979
31980     setContent : function(content, loadScripts){
31981         this.resizeEl.update(content, loadScripts);
31982     }
31983
31984 });
31985
31986
31987
31988 /**
31989  * @class Roo.TreePanel
31990  * @extends Roo.ContentPanel
31991  * Treepanel component
31992  * 
31993  * @constructor
31994  * Create a new TreePanel. - defaults to fit/scoll contents.
31995  * @param {String/Object} config A string to set only the panel's title, or a config object
31996  */
31997 Roo.TreePanel = function(config){
31998     var el = config.el;
31999     var tree = config.tree;
32000     delete config.tree; 
32001     delete config.el; // hopefull!
32002     
32003     // wrapper for IE7 strict & safari scroll issue
32004     
32005     var treeEl = el.createChild();
32006     config.resizeEl = treeEl;
32007     
32008     
32009     
32010     Roo.TreePanel.superclass.constructor.call(this, el, config);
32011  
32012  
32013     this.tree = new Roo.tree.TreePanel(treeEl , tree);
32014     //console.log(tree);
32015     this.on('activate', function()
32016     {
32017         if (this.tree.rendered) {
32018             return;
32019         }
32020         //console.log('render tree');
32021         this.tree.render();
32022     });
32023     // this should not be needed.. - it's actually the 'el' that resizes?
32024     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
32025     
32026     //this.on('resize',  function (cp, w, h) {
32027     //        this.tree.innerCt.setWidth(w);
32028     //        this.tree.innerCt.setHeight(h);
32029     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
32030     //});
32031
32032         
32033     
32034 };
32035
32036 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
32037     fitToFrame : true,
32038     autoScroll : true,
32039     /*
32040      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
32041      */
32042     tree : false
32043
32044 });
32045
32046
32047
32048
32049
32050
32051
32052
32053
32054
32055
32056 /*
32057  * Based on:
32058  * Ext JS Library 1.1.1
32059  * Copyright(c) 2006-2007, Ext JS, LLC.
32060  *
32061  * Originally Released Under LGPL - original licence link has changed is not relivant.
32062  *
32063  * Fork - LGPL
32064  * <script type="text/javascript">
32065  */
32066  
32067
32068 /**
32069  * @class Roo.ReaderLayout
32070  * @extends Roo.BorderLayout
32071  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
32072  * center region containing two nested regions (a top one for a list view and one for item preview below),
32073  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
32074  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
32075  * expedites the setup of the overall layout and regions for this common application style.
32076  * Example:
32077  <pre><code>
32078 var reader = new Roo.ReaderLayout();
32079 var CP = Roo.ContentPanel;  // shortcut for adding
32080
32081 reader.beginUpdate();
32082 reader.add("north", new CP("north", "North"));
32083 reader.add("west", new CP("west", {title: "West"}));
32084 reader.add("east", new CP("east", {title: "East"}));
32085
32086 reader.regions.listView.add(new CP("listView", "List"));
32087 reader.regions.preview.add(new CP("preview", "Preview"));
32088 reader.endUpdate();
32089 </code></pre>
32090 * @constructor
32091 * Create a new ReaderLayout
32092 * @param {Object} config Configuration options
32093 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
32094 * document.body if omitted)
32095 */
32096 Roo.ReaderLayout = function(config, renderTo){
32097     var c = config || {size:{}};
32098     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
32099         north: c.north !== false ? Roo.apply({
32100             split:false,
32101             initialSize: 32,
32102             titlebar: false
32103         }, c.north) : false,
32104         west: c.west !== false ? Roo.apply({
32105             split:true,
32106             initialSize: 200,
32107             minSize: 175,
32108             maxSize: 400,
32109             titlebar: true,
32110             collapsible: true,
32111             animate: true,
32112             margins:{left:5,right:0,bottom:5,top:5},
32113             cmargins:{left:5,right:5,bottom:5,top:5}
32114         }, c.west) : false,
32115         east: c.east !== false ? Roo.apply({
32116             split:true,
32117             initialSize: 200,
32118             minSize: 175,
32119             maxSize: 400,
32120             titlebar: true,
32121             collapsible: true,
32122             animate: true,
32123             margins:{left:0,right:5,bottom:5,top:5},
32124             cmargins:{left:5,right:5,bottom:5,top:5}
32125         }, c.east) : false,
32126         center: Roo.apply({
32127             tabPosition: 'top',
32128             autoScroll:false,
32129             closeOnTab: true,
32130             titlebar:false,
32131             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32132         }, c.center)
32133     });
32134
32135     this.el.addClass('x-reader');
32136
32137     this.beginUpdate();
32138
32139     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32140         south: c.preview !== false ? Roo.apply({
32141             split:true,
32142             initialSize: 200,
32143             minSize: 100,
32144             autoScroll:true,
32145             collapsible:true,
32146             titlebar: true,
32147             cmargins:{top:5,left:0, right:0, bottom:0}
32148         }, c.preview) : false,
32149         center: Roo.apply({
32150             autoScroll:false,
32151             titlebar:false,
32152             minHeight:200
32153         }, c.listView)
32154     });
32155     this.add('center', new Roo.NestedLayoutPanel(inner,
32156             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
32157
32158     this.endUpdate();
32159
32160     this.regions.preview = inner.getRegion('south');
32161     this.regions.listView = inner.getRegion('center');
32162 };
32163
32164 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32165  * Based on:
32166  * Ext JS Library 1.1.1
32167  * Copyright(c) 2006-2007, Ext JS, LLC.
32168  *
32169  * Originally Released Under LGPL - original licence link has changed is not relivant.
32170  *
32171  * Fork - LGPL
32172  * <script type="text/javascript">
32173  */
32174  
32175 /**
32176  * @class Roo.grid.Grid
32177  * @extends Roo.util.Observable
32178  * This class represents the primary interface of a component based grid control.
32179  * <br><br>Usage:<pre><code>
32180  var grid = new Roo.grid.Grid("my-container-id", {
32181      ds: myDataStore,
32182      cm: myColModel,
32183      selModel: mySelectionModel,
32184      autoSizeColumns: true,
32185      monitorWindowResize: false,
32186      trackMouseOver: true
32187  });
32188  // set any options
32189  grid.render();
32190  * </code></pre>
32191  * <b>Common Problems:</b><br/>
32192  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32193  * element will correct this<br/>
32194  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32195  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32196  * are unpredictable.<br/>
32197  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32198  * grid to calculate dimensions/offsets.<br/>
32199   * @constructor
32200  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32201  * The container MUST have some type of size defined for the grid to fill. The container will be
32202  * automatically set to position relative if it isn't already.
32203  * @param {Object} config A config object that sets properties on this grid.
32204  */
32205 Roo.grid.Grid = function(container, config){
32206         // initialize the container
32207         this.container = Roo.get(container);
32208         this.container.update("");
32209         this.container.setStyle("overflow", "hidden");
32210     this.container.addClass('x-grid-container');
32211
32212     this.id = this.container.id;
32213
32214     Roo.apply(this, config);
32215     // check and correct shorthanded configs
32216     if(this.ds){
32217         this.dataSource = this.ds;
32218         delete this.ds;
32219     }
32220     if(this.cm){
32221         this.colModel = this.cm;
32222         delete this.cm;
32223     }
32224     if(this.sm){
32225         this.selModel = this.sm;
32226         delete this.sm;
32227     }
32228
32229     if (this.selModel) {
32230         this.selModel = Roo.factory(this.selModel, Roo.grid);
32231         this.sm = this.selModel;
32232         this.sm.xmodule = this.xmodule || false;
32233     }
32234     if (typeof(this.colModel.config) == 'undefined') {
32235         this.colModel = new Roo.grid.ColumnModel(this.colModel);
32236         this.cm = this.colModel;
32237         this.cm.xmodule = this.xmodule || false;
32238     }
32239     if (this.dataSource) {
32240         this.dataSource= Roo.factory(this.dataSource, Roo.data);
32241         this.ds = this.dataSource;
32242         this.ds.xmodule = this.xmodule || false;
32243          
32244     }
32245     
32246     
32247     
32248     if(this.width){
32249         this.container.setWidth(this.width);
32250     }
32251
32252     if(this.height){
32253         this.container.setHeight(this.height);
32254     }
32255     /** @private */
32256         this.addEvents({
32257         // raw events
32258         /**
32259          * @event click
32260          * The raw click event for the entire grid.
32261          * @param {Roo.EventObject} e
32262          */
32263         "click" : true,
32264         /**
32265          * @event dblclick
32266          * The raw dblclick event for the entire grid.
32267          * @param {Roo.EventObject} e
32268          */
32269         "dblclick" : true,
32270         /**
32271          * @event contextmenu
32272          * The raw contextmenu event for the entire grid.
32273          * @param {Roo.EventObject} e
32274          */
32275         "contextmenu" : true,
32276         /**
32277          * @event mousedown
32278          * The raw mousedown event for the entire grid.
32279          * @param {Roo.EventObject} e
32280          */
32281         "mousedown" : true,
32282         /**
32283          * @event mouseup
32284          * The raw mouseup event for the entire grid.
32285          * @param {Roo.EventObject} e
32286          */
32287         "mouseup" : true,
32288         /**
32289          * @event mouseover
32290          * The raw mouseover event for the entire grid.
32291          * @param {Roo.EventObject} e
32292          */
32293         "mouseover" : true,
32294         /**
32295          * @event mouseout
32296          * The raw mouseout event for the entire grid.
32297          * @param {Roo.EventObject} e
32298          */
32299         "mouseout" : true,
32300         /**
32301          * @event keypress
32302          * The raw keypress event for the entire grid.
32303          * @param {Roo.EventObject} e
32304          */
32305         "keypress" : true,
32306         /**
32307          * @event keydown
32308          * The raw keydown event for the entire grid.
32309          * @param {Roo.EventObject} e
32310          */
32311         "keydown" : true,
32312
32313         // custom events
32314
32315         /**
32316          * @event cellclick
32317          * Fires when a cell is clicked
32318          * @param {Grid} this
32319          * @param {Number} rowIndex
32320          * @param {Number} columnIndex
32321          * @param {Roo.EventObject} e
32322          */
32323         "cellclick" : true,
32324         /**
32325          * @event celldblclick
32326          * Fires when a cell is double clicked
32327          * @param {Grid} this
32328          * @param {Number} rowIndex
32329          * @param {Number} columnIndex
32330          * @param {Roo.EventObject} e
32331          */
32332         "celldblclick" : true,
32333         /**
32334          * @event rowclick
32335          * Fires when a row is clicked
32336          * @param {Grid} this
32337          * @param {Number} rowIndex
32338          * @param {Roo.EventObject} e
32339          */
32340         "rowclick" : true,
32341         /**
32342          * @event rowdblclick
32343          * Fires when a row is double clicked
32344          * @param {Grid} this
32345          * @param {Number} rowIndex
32346          * @param {Roo.EventObject} e
32347          */
32348         "rowdblclick" : true,
32349         /**
32350          * @event headerclick
32351          * Fires when a header is clicked
32352          * @param {Grid} this
32353          * @param {Number} columnIndex
32354          * @param {Roo.EventObject} e
32355          */
32356         "headerclick" : true,
32357         /**
32358          * @event headerdblclick
32359          * Fires when a header cell is double clicked
32360          * @param {Grid} this
32361          * @param {Number} columnIndex
32362          * @param {Roo.EventObject} e
32363          */
32364         "headerdblclick" : true,
32365         /**
32366          * @event rowcontextmenu
32367          * Fires when a row is right clicked
32368          * @param {Grid} this
32369          * @param {Number} rowIndex
32370          * @param {Roo.EventObject} e
32371          */
32372         "rowcontextmenu" : true,
32373         /**
32374          * @event cellcontextmenu
32375          * Fires when a cell is right clicked
32376          * @param {Grid} this
32377          * @param {Number} rowIndex
32378          * @param {Number} cellIndex
32379          * @param {Roo.EventObject} e
32380          */
32381          "cellcontextmenu" : true,
32382         /**
32383          * @event headercontextmenu
32384          * Fires when a header is right clicked
32385          * @param {Grid} this
32386          * @param {Number} columnIndex
32387          * @param {Roo.EventObject} e
32388          */
32389         "headercontextmenu" : true,
32390         /**
32391          * @event bodyscroll
32392          * Fires when the body element is scrolled
32393          * @param {Number} scrollLeft
32394          * @param {Number} scrollTop
32395          */
32396         "bodyscroll" : true,
32397         /**
32398          * @event columnresize
32399          * Fires when the user resizes a column
32400          * @param {Number} columnIndex
32401          * @param {Number} newSize
32402          */
32403         "columnresize" : true,
32404         /**
32405          * @event columnmove
32406          * Fires when the user moves a column
32407          * @param {Number} oldIndex
32408          * @param {Number} newIndex
32409          */
32410         "columnmove" : true,
32411         /**
32412          * @event startdrag
32413          * Fires when row(s) start being dragged
32414          * @param {Grid} this
32415          * @param {Roo.GridDD} dd The drag drop object
32416          * @param {event} e The raw browser event
32417          */
32418         "startdrag" : true,
32419         /**
32420          * @event enddrag
32421          * Fires when a drag operation is complete
32422          * @param {Grid} this
32423          * @param {Roo.GridDD} dd The drag drop object
32424          * @param {event} e The raw browser event
32425          */
32426         "enddrag" : true,
32427         /**
32428          * @event dragdrop
32429          * Fires when dragged row(s) are dropped on a valid DD target
32430          * @param {Grid} this
32431          * @param {Roo.GridDD} dd The drag drop object
32432          * @param {String} targetId The target drag drop object
32433          * @param {event} e The raw browser event
32434          */
32435         "dragdrop" : true,
32436         /**
32437          * @event dragover
32438          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32439          * @param {Grid} this
32440          * @param {Roo.GridDD} dd The drag drop object
32441          * @param {String} targetId The target drag drop object
32442          * @param {event} e The raw browser event
32443          */
32444         "dragover" : true,
32445         /**
32446          * @event dragenter
32447          *  Fires when the dragged row(s) first cross another DD target while being dragged
32448          * @param {Grid} this
32449          * @param {Roo.GridDD} dd The drag drop object
32450          * @param {String} targetId The target drag drop object
32451          * @param {event} e The raw browser event
32452          */
32453         "dragenter" : true,
32454         /**
32455          * @event dragout
32456          * Fires when the dragged row(s) leave another DD target while being dragged
32457          * @param {Grid} this
32458          * @param {Roo.GridDD} dd The drag drop object
32459          * @param {String} targetId The target drag drop object
32460          * @param {event} e The raw browser event
32461          */
32462         "dragout" : true,
32463         /**
32464          * @event rowclass
32465          * Fires when a row is rendered, so you can change add a style to it.
32466          * @param {GridView} gridview   The grid view
32467          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
32468          */
32469         'rowclass' : true,
32470
32471         /**
32472          * @event render
32473          * Fires when the grid is rendered
32474          * @param {Grid} grid
32475          */
32476         'render' : true
32477     });
32478
32479     Roo.grid.Grid.superclass.constructor.call(this);
32480 };
32481 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32482     
32483     /**
32484          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
32485          */
32486         /**
32487          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
32488          */
32489         /**
32490          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
32491          */
32492         /**
32493          * @cfg {Roo.grid.Store} ds The data store for the grid
32494          */
32495         /**
32496          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
32497          */
32498         /**
32499      * @cfg {String} ddGroup - drag drop group.
32500      */
32501       /**
32502      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
32503      */
32504
32505     /**
32506      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32507      */
32508     minColumnWidth : 25,
32509
32510     /**
32511      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32512      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32513      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32514      */
32515     autoSizeColumns : false,
32516
32517     /**
32518      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32519      */
32520     autoSizeHeaders : true,
32521
32522     /**
32523      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32524      */
32525     monitorWindowResize : true,
32526
32527     /**
32528      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32529      * rows measured to get a columns size. Default is 0 (all rows).
32530      */
32531     maxRowsToMeasure : 0,
32532
32533     /**
32534      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32535      */
32536     trackMouseOver : true,
32537
32538     /**
32539     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32540     */
32541       /**
32542     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
32543     */
32544     
32545     /**
32546     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32547     */
32548     enableDragDrop : false,
32549     
32550     /**
32551     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32552     */
32553     enableColumnMove : true,
32554     
32555     /**
32556     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32557     */
32558     enableColumnHide : true,
32559     
32560     /**
32561     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32562     */
32563     enableRowHeightSync : false,
32564     
32565     /**
32566     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32567     */
32568     stripeRows : true,
32569     
32570     /**
32571     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32572     */
32573     autoHeight : false,
32574
32575     /**
32576      * @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.
32577      */
32578     autoExpandColumn : false,
32579
32580     /**
32581     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32582     * Default is 50.
32583     */
32584     autoExpandMin : 50,
32585
32586     /**
32587     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32588     */
32589     autoExpandMax : 1000,
32590
32591     /**
32592     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32593     */
32594     view : null,
32595
32596     /**
32597     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32598     */
32599     loadMask : false,
32600     /**
32601     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32602     */
32603     dropTarget: false,
32604     
32605    
32606     
32607     // private
32608     rendered : false,
32609
32610     /**
32611     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32612     * of a fixed width. Default is false.
32613     */
32614     /**
32615     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32616     */
32617     
32618     
32619     /**
32620     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32621     * %0 is replaced with the number of selected rows.
32622     */
32623     ddText : "{0} selected row{1}",
32624     
32625     
32626     /**
32627      * Called once after all setup has been completed and the grid is ready to be rendered.
32628      * @return {Roo.grid.Grid} this
32629      */
32630     render : function()
32631     {
32632         var c = this.container;
32633         // try to detect autoHeight/width mode
32634         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32635             this.autoHeight = true;
32636         }
32637         var view = this.getView();
32638         view.init(this);
32639
32640         c.on("click", this.onClick, this);
32641         c.on("dblclick", this.onDblClick, this);
32642         c.on("contextmenu", this.onContextMenu, this);
32643         c.on("keydown", this.onKeyDown, this);
32644         if (Roo.isTouch) {
32645             c.on("touchstart", this.onTouchStart, this);
32646         }
32647
32648         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32649
32650         this.getSelectionModel().init(this);
32651
32652         view.render();
32653
32654         if(this.loadMask){
32655             this.loadMask = new Roo.LoadMask(this.container,
32656                     Roo.apply({store:this.dataSource}, this.loadMask));
32657         }
32658         
32659         
32660         if (this.toolbar && this.toolbar.xtype) {
32661             this.toolbar.container = this.getView().getHeaderPanel(true);
32662             this.toolbar = new Roo.Toolbar(this.toolbar);
32663         }
32664         if (this.footer && this.footer.xtype) {
32665             this.footer.dataSource = this.getDataSource();
32666             this.footer.container = this.getView().getFooterPanel(true);
32667             this.footer = Roo.factory(this.footer, Roo);
32668         }
32669         if (this.dropTarget && this.dropTarget.xtype) {
32670             delete this.dropTarget.xtype;
32671             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32672         }
32673         
32674         
32675         this.rendered = true;
32676         this.fireEvent('render', this);
32677         return this;
32678     },
32679
32680     /**
32681      * Reconfigures the grid to use a different Store and Column Model.
32682      * The View will be bound to the new objects and refreshed.
32683      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32684      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32685      */
32686     reconfigure : function(dataSource, colModel){
32687         if(this.loadMask){
32688             this.loadMask.destroy();
32689             this.loadMask = new Roo.LoadMask(this.container,
32690                     Roo.apply({store:dataSource}, this.loadMask));
32691         }
32692         this.view.bind(dataSource, colModel);
32693         this.dataSource = dataSource;
32694         this.colModel = colModel;
32695         this.view.refresh(true);
32696     },
32697     /**
32698      * addColumns
32699      * Add's a column, default at the end..
32700      
32701      * @param {int} position to add (default end)
32702      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
32703      */
32704     addColumns : function(pos, ar)
32705     {
32706         
32707         for (var i =0;i< ar.length;i++) {
32708             var cfg = ar[i];
32709             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
32710             this.cm.lookup[cfg.id] = cfg;
32711         }
32712         
32713         
32714         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
32715             pos = this.cm.config.length; //this.cm.config.push(cfg);
32716         } 
32717         pos = Math.max(0,pos);
32718         ar.unshift(0);
32719         ar.unshift(pos);
32720         this.cm.config.splice.apply(this.cm.config, ar);
32721         
32722         
32723         
32724         this.view.generateRules(this.cm);
32725         this.view.refresh(true);
32726         
32727     },
32728     
32729     
32730     
32731     
32732     // private
32733     onKeyDown : function(e){
32734         this.fireEvent("keydown", e);
32735     },
32736
32737     /**
32738      * Destroy this grid.
32739      * @param {Boolean} removeEl True to remove the element
32740      */
32741     destroy : function(removeEl, keepListeners){
32742         if(this.loadMask){
32743             this.loadMask.destroy();
32744         }
32745         var c = this.container;
32746         c.removeAllListeners();
32747         this.view.destroy();
32748         this.colModel.purgeListeners();
32749         if(!keepListeners){
32750             this.purgeListeners();
32751         }
32752         c.update("");
32753         if(removeEl === true){
32754             c.remove();
32755         }
32756     },
32757
32758     // private
32759     processEvent : function(name, e){
32760         // does this fire select???
32761         //Roo.log('grid:processEvent '  + name);
32762         
32763         if (name != 'touchstart' ) {
32764             this.fireEvent(name, e);    
32765         }
32766         
32767         var t = e.getTarget();
32768         var v = this.view;
32769         var header = v.findHeaderIndex(t);
32770         if(header !== false){
32771             var ename = name == 'touchstart' ? 'click' : name;
32772              
32773             this.fireEvent("header" + ename, this, header, e);
32774         }else{
32775             var row = v.findRowIndex(t);
32776             var cell = v.findCellIndex(t);
32777             if (name == 'touchstart') {
32778                 // first touch is always a click.
32779                 // hopefull this happens after selection is updated.?
32780                 name = false;
32781                 
32782                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32783                     var cs = this.selModel.getSelectedCell();
32784                     if (row == cs[0] && cell == cs[1]){
32785                         name = 'dblclick';
32786                     }
32787                 }
32788                 if (typeof(this.selModel.getSelections) != 'undefined') {
32789                     var cs = this.selModel.getSelections();
32790                     var ds = this.dataSource;
32791                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
32792                         name = 'dblclick';
32793                     }
32794                 }
32795                 if (!name) {
32796                     return;
32797                 }
32798             }
32799             
32800             
32801             if(row !== false){
32802                 this.fireEvent("row" + name, this, row, e);
32803                 if(cell !== false){
32804                     this.fireEvent("cell" + name, this, row, cell, e);
32805                 }
32806             }
32807         }
32808     },
32809
32810     // private
32811     onClick : function(e){
32812         this.processEvent("click", e);
32813     },
32814    // private
32815     onTouchStart : function(e){
32816         this.processEvent("touchstart", e);
32817     },
32818
32819     // private
32820     onContextMenu : function(e, t){
32821         this.processEvent("contextmenu", e);
32822     },
32823
32824     // private
32825     onDblClick : function(e){
32826         this.processEvent("dblclick", e);
32827     },
32828
32829     // private
32830     walkCells : function(row, col, step, fn, scope){
32831         var cm = this.colModel, clen = cm.getColumnCount();
32832         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32833         if(step < 0){
32834             if(col < 0){
32835                 row--;
32836                 first = false;
32837             }
32838             while(row >= 0){
32839                 if(!first){
32840                     col = clen-1;
32841                 }
32842                 first = false;
32843                 while(col >= 0){
32844                     if(fn.call(scope || this, row, col, cm) === true){
32845                         return [row, col];
32846                     }
32847                     col--;
32848                 }
32849                 row--;
32850             }
32851         } else {
32852             if(col >= clen){
32853                 row++;
32854                 first = false;
32855             }
32856             while(row < rlen){
32857                 if(!first){
32858                     col = 0;
32859                 }
32860                 first = false;
32861                 while(col < clen){
32862                     if(fn.call(scope || this, row, col, cm) === true){
32863                         return [row, col];
32864                     }
32865                     col++;
32866                 }
32867                 row++;
32868             }
32869         }
32870         return null;
32871     },
32872
32873     // private
32874     getSelections : function(){
32875         return this.selModel.getSelections();
32876     },
32877
32878     /**
32879      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32880      * but if manual update is required this method will initiate it.
32881      */
32882     autoSize : function(){
32883         if(this.rendered){
32884             this.view.layout();
32885             if(this.view.adjustForScroll){
32886                 this.view.adjustForScroll();
32887             }
32888         }
32889     },
32890
32891     /**
32892      * Returns the grid's underlying element.
32893      * @return {Element} The element
32894      */
32895     getGridEl : function(){
32896         return this.container;
32897     },
32898
32899     // private for compatibility, overridden by editor grid
32900     stopEditing : function(){},
32901
32902     /**
32903      * Returns the grid's SelectionModel.
32904      * @return {SelectionModel}
32905      */
32906     getSelectionModel : function(){
32907         if(!this.selModel){
32908             this.selModel = new Roo.grid.RowSelectionModel();
32909         }
32910         return this.selModel;
32911     },
32912
32913     /**
32914      * Returns the grid's DataSource.
32915      * @return {DataSource}
32916      */
32917     getDataSource : function(){
32918         return this.dataSource;
32919     },
32920
32921     /**
32922      * Returns the grid's ColumnModel.
32923      * @return {ColumnModel}
32924      */
32925     getColumnModel : function(){
32926         return this.colModel;
32927     },
32928
32929     /**
32930      * Returns the grid's GridView object.
32931      * @return {GridView}
32932      */
32933     getView : function(){
32934         if(!this.view){
32935             this.view = new Roo.grid.GridView(this.viewConfig);
32936             this.relayEvents(this.view, [
32937                 "beforerowremoved", "beforerowsinserted",
32938                 "beforerefresh", "rowremoved",
32939                 "rowsinserted", "rowupdated" ,"refresh"
32940             ]);
32941         }
32942         return this.view;
32943     },
32944     /**
32945      * Called to get grid's drag proxy text, by default returns this.ddText.
32946      * Override this to put something different in the dragged text.
32947      * @return {String}
32948      */
32949     getDragDropText : function(){
32950         var count = this.selModel.getCount();
32951         return String.format(this.ddText, count, count == 1 ? '' : 's');
32952     }
32953 });
32954 /*
32955  * Based on:
32956  * Ext JS Library 1.1.1
32957  * Copyright(c) 2006-2007, Ext JS, LLC.
32958  *
32959  * Originally Released Under LGPL - original licence link has changed is not relivant.
32960  *
32961  * Fork - LGPL
32962  * <script type="text/javascript">
32963  */
32964  /**
32965  * @class Roo.grid.AbstractGridView
32966  * @extends Roo.util.Observable
32967  * @abstract
32968  * Abstract base class for grid Views
32969  * @constructor
32970  */
32971 Roo.grid.AbstractGridView = function(){
32972         this.grid = null;
32973         
32974         this.events = {
32975             "beforerowremoved" : true,
32976             "beforerowsinserted" : true,
32977             "beforerefresh" : true,
32978             "rowremoved" : true,
32979             "rowsinserted" : true,
32980             "rowupdated" : true,
32981             "refresh" : true
32982         };
32983     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32984 };
32985
32986 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32987     rowClass : "x-grid-row",
32988     cellClass : "x-grid-cell",
32989     tdClass : "x-grid-td",
32990     hdClass : "x-grid-hd",
32991     splitClass : "x-grid-hd-split",
32992     
32993     init: function(grid){
32994         this.grid = grid;
32995                 var cid = this.grid.getGridEl().id;
32996         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32997         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32998         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32999         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
33000         },
33001         
33002     getColumnRenderers : function(){
33003         var renderers = [];
33004         var cm = this.grid.colModel;
33005         var colCount = cm.getColumnCount();
33006         for(var i = 0; i < colCount; i++){
33007             renderers[i] = cm.getRenderer(i);
33008         }
33009         return renderers;
33010     },
33011     
33012     getColumnIds : function(){
33013         var ids = [];
33014         var cm = this.grid.colModel;
33015         var colCount = cm.getColumnCount();
33016         for(var i = 0; i < colCount; i++){
33017             ids[i] = cm.getColumnId(i);
33018         }
33019         return ids;
33020     },
33021     
33022     getDataIndexes : function(){
33023         if(!this.indexMap){
33024             this.indexMap = this.buildIndexMap();
33025         }
33026         return this.indexMap.colToData;
33027     },
33028     
33029     getColumnIndexByDataIndex : function(dataIndex){
33030         if(!this.indexMap){
33031             this.indexMap = this.buildIndexMap();
33032         }
33033         return this.indexMap.dataToCol[dataIndex];
33034     },
33035     
33036     /**
33037      * Set a css style for a column dynamically. 
33038      * @param {Number} colIndex The index of the column
33039      * @param {String} name The css property name
33040      * @param {String} value The css value
33041      */
33042     setCSSStyle : function(colIndex, name, value){
33043         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
33044         Roo.util.CSS.updateRule(selector, name, value);
33045     },
33046     
33047     generateRules : function(cm){
33048         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
33049         Roo.util.CSS.removeStyleSheet(rulesId);
33050         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33051             var cid = cm.getColumnId(i);
33052             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
33053                          this.tdSelector, cid, " {\n}\n",
33054                          this.hdSelector, cid, " {\n}\n",
33055                          this.splitSelector, cid, " {\n}\n");
33056         }
33057         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33058     }
33059 });/*
33060  * Based on:
33061  * Ext JS Library 1.1.1
33062  * Copyright(c) 2006-2007, Ext JS, LLC.
33063  *
33064  * Originally Released Under LGPL - original licence link has changed is not relivant.
33065  *
33066  * Fork - LGPL
33067  * <script type="text/javascript">
33068  */
33069
33070 // private
33071 // This is a support class used internally by the Grid components
33072 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
33073     this.grid = grid;
33074     this.view = grid.getView();
33075     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33076     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
33077     if(hd2){
33078         this.setHandleElId(Roo.id(hd));
33079         this.setOuterHandleElId(Roo.id(hd2));
33080     }
33081     this.scroll = false;
33082 };
33083 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
33084     maxDragWidth: 120,
33085     getDragData : function(e){
33086         var t = Roo.lib.Event.getTarget(e);
33087         var h = this.view.findHeaderCell(t);
33088         if(h){
33089             return {ddel: h.firstChild, header:h};
33090         }
33091         return false;
33092     },
33093
33094     onInitDrag : function(e){
33095         this.view.headersDisabled = true;
33096         var clone = this.dragData.ddel.cloneNode(true);
33097         clone.id = Roo.id();
33098         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
33099         this.proxy.update(clone);
33100         return true;
33101     },
33102
33103     afterValidDrop : function(){
33104         var v = this.view;
33105         setTimeout(function(){
33106             v.headersDisabled = false;
33107         }, 50);
33108     },
33109
33110     afterInvalidDrop : function(){
33111         var v = this.view;
33112         setTimeout(function(){
33113             v.headersDisabled = false;
33114         }, 50);
33115     }
33116 });
33117 /*
33118  * Based on:
33119  * Ext JS Library 1.1.1
33120  * Copyright(c) 2006-2007, Ext JS, LLC.
33121  *
33122  * Originally Released Under LGPL - original licence link has changed is not relivant.
33123  *
33124  * Fork - LGPL
33125  * <script type="text/javascript">
33126  */
33127 // private
33128 // This is a support class used internally by the Grid components
33129 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
33130     this.grid = grid;
33131     this.view = grid.getView();
33132     // split the proxies so they don't interfere with mouse events
33133     this.proxyTop = Roo.DomHelper.append(document.body, {
33134         cls:"col-move-top", html:"&#160;"
33135     }, true);
33136     this.proxyBottom = Roo.DomHelper.append(document.body, {
33137         cls:"col-move-bottom", html:"&#160;"
33138     }, true);
33139     this.proxyTop.hide = this.proxyBottom.hide = function(){
33140         this.setLeftTop(-100,-100);
33141         this.setStyle("visibility", "hidden");
33142     };
33143     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33144     // temporarily disabled
33145     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
33146     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
33147 };
33148 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
33149     proxyOffsets : [-4, -9],
33150     fly: Roo.Element.fly,
33151
33152     getTargetFromEvent : function(e){
33153         var t = Roo.lib.Event.getTarget(e);
33154         var cindex = this.view.findCellIndex(t);
33155         if(cindex !== false){
33156             return this.view.getHeaderCell(cindex);
33157         }
33158         return null;
33159     },
33160
33161     nextVisible : function(h){
33162         var v = this.view, cm = this.grid.colModel;
33163         h = h.nextSibling;
33164         while(h){
33165             if(!cm.isHidden(v.getCellIndex(h))){
33166                 return h;
33167             }
33168             h = h.nextSibling;
33169         }
33170         return null;
33171     },
33172
33173     prevVisible : function(h){
33174         var v = this.view, cm = this.grid.colModel;
33175         h = h.prevSibling;
33176         while(h){
33177             if(!cm.isHidden(v.getCellIndex(h))){
33178                 return h;
33179             }
33180             h = h.prevSibling;
33181         }
33182         return null;
33183     },
33184
33185     positionIndicator : function(h, n, e){
33186         var x = Roo.lib.Event.getPageX(e);
33187         var r = Roo.lib.Dom.getRegion(n.firstChild);
33188         var px, pt, py = r.top + this.proxyOffsets[1];
33189         if((r.right - x) <= (r.right-r.left)/2){
33190             px = r.right+this.view.borderWidth;
33191             pt = "after";
33192         }else{
33193             px = r.left;
33194             pt = "before";
33195         }
33196         var oldIndex = this.view.getCellIndex(h);
33197         var newIndex = this.view.getCellIndex(n);
33198
33199         if(this.grid.colModel.isFixed(newIndex)){
33200             return false;
33201         }
33202
33203         var locked = this.grid.colModel.isLocked(newIndex);
33204
33205         if(pt == "after"){
33206             newIndex++;
33207         }
33208         if(oldIndex < newIndex){
33209             newIndex--;
33210         }
33211         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33212             return false;
33213         }
33214         px +=  this.proxyOffsets[0];
33215         this.proxyTop.setLeftTop(px, py);
33216         this.proxyTop.show();
33217         if(!this.bottomOffset){
33218             this.bottomOffset = this.view.mainHd.getHeight();
33219         }
33220         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33221         this.proxyBottom.show();
33222         return pt;
33223     },
33224
33225     onNodeEnter : function(n, dd, e, data){
33226         if(data.header != n){
33227             this.positionIndicator(data.header, n, e);
33228         }
33229     },
33230
33231     onNodeOver : function(n, dd, e, data){
33232         var result = false;
33233         if(data.header != n){
33234             result = this.positionIndicator(data.header, n, e);
33235         }
33236         if(!result){
33237             this.proxyTop.hide();
33238             this.proxyBottom.hide();
33239         }
33240         return result ? this.dropAllowed : this.dropNotAllowed;
33241     },
33242
33243     onNodeOut : function(n, dd, e, data){
33244         this.proxyTop.hide();
33245         this.proxyBottom.hide();
33246     },
33247
33248     onNodeDrop : function(n, dd, e, data){
33249         var h = data.header;
33250         if(h != n){
33251             var cm = this.grid.colModel;
33252             var x = Roo.lib.Event.getPageX(e);
33253             var r = Roo.lib.Dom.getRegion(n.firstChild);
33254             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33255             var oldIndex = this.view.getCellIndex(h);
33256             var newIndex = this.view.getCellIndex(n);
33257             var locked = cm.isLocked(newIndex);
33258             if(pt == "after"){
33259                 newIndex++;
33260             }
33261             if(oldIndex < newIndex){
33262                 newIndex--;
33263             }
33264             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33265                 return false;
33266             }
33267             cm.setLocked(oldIndex, locked, true);
33268             cm.moveColumn(oldIndex, newIndex);
33269             this.grid.fireEvent("columnmove", oldIndex, newIndex);
33270             return true;
33271         }
33272         return false;
33273     }
33274 });
33275 /*
33276  * Based on:
33277  * Ext JS Library 1.1.1
33278  * Copyright(c) 2006-2007, Ext JS, LLC.
33279  *
33280  * Originally Released Under LGPL - original licence link has changed is not relivant.
33281  *
33282  * Fork - LGPL
33283  * <script type="text/javascript">
33284  */
33285   
33286 /**
33287  * @class Roo.grid.GridView
33288  * @extends Roo.util.Observable
33289  *
33290  * @constructor
33291  * @param {Object} config
33292  */
33293 Roo.grid.GridView = function(config){
33294     Roo.grid.GridView.superclass.constructor.call(this);
33295     this.el = null;
33296
33297     Roo.apply(this, config);
33298 };
33299
33300 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33301
33302     unselectable :  'unselectable="on"',
33303     unselectableCls :  'x-unselectable',
33304     
33305     
33306     rowClass : "x-grid-row",
33307
33308     cellClass : "x-grid-col",
33309
33310     tdClass : "x-grid-td",
33311
33312     hdClass : "x-grid-hd",
33313
33314     splitClass : "x-grid-split",
33315
33316     sortClasses : ["sort-asc", "sort-desc"],
33317
33318     enableMoveAnim : false,
33319
33320     hlColor: "C3DAF9",
33321
33322     dh : Roo.DomHelper,
33323
33324     fly : Roo.Element.fly,
33325
33326     css : Roo.util.CSS,
33327
33328     borderWidth: 1,
33329
33330     splitOffset: 3,
33331
33332     scrollIncrement : 22,
33333
33334     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33335
33336     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33337
33338     bind : function(ds, cm){
33339         if(this.ds){
33340             this.ds.un("load", this.onLoad, this);
33341             this.ds.un("datachanged", this.onDataChange, this);
33342             this.ds.un("add", this.onAdd, this);
33343             this.ds.un("remove", this.onRemove, this);
33344             this.ds.un("update", this.onUpdate, this);
33345             this.ds.un("clear", this.onClear, this);
33346         }
33347         if(ds){
33348             ds.on("load", this.onLoad, this);
33349             ds.on("datachanged", this.onDataChange, this);
33350             ds.on("add", this.onAdd, this);
33351             ds.on("remove", this.onRemove, this);
33352             ds.on("update", this.onUpdate, this);
33353             ds.on("clear", this.onClear, this);
33354         }
33355         this.ds = ds;
33356
33357         if(this.cm){
33358             this.cm.un("widthchange", this.onColWidthChange, this);
33359             this.cm.un("headerchange", this.onHeaderChange, this);
33360             this.cm.un("hiddenchange", this.onHiddenChange, this);
33361             this.cm.un("columnmoved", this.onColumnMove, this);
33362             this.cm.un("columnlockchange", this.onColumnLock, this);
33363         }
33364         if(cm){
33365             this.generateRules(cm);
33366             cm.on("widthchange", this.onColWidthChange, this);
33367             cm.on("headerchange", this.onHeaderChange, this);
33368             cm.on("hiddenchange", this.onHiddenChange, this);
33369             cm.on("columnmoved", this.onColumnMove, this);
33370             cm.on("columnlockchange", this.onColumnLock, this);
33371         }
33372         this.cm = cm;
33373     },
33374
33375     init: function(grid){
33376         Roo.grid.GridView.superclass.init.call(this, grid);
33377
33378         this.bind(grid.dataSource, grid.colModel);
33379
33380         grid.on("headerclick", this.handleHeaderClick, this);
33381
33382         if(grid.trackMouseOver){
33383             grid.on("mouseover", this.onRowOver, this);
33384             grid.on("mouseout", this.onRowOut, this);
33385         }
33386         grid.cancelTextSelection = function(){};
33387         this.gridId = grid.id;
33388
33389         var tpls = this.templates || {};
33390
33391         if(!tpls.master){
33392             tpls.master = new Roo.Template(
33393                '<div class="x-grid" hidefocus="true">',
33394                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
33395                   '<div class="x-grid-topbar"></div>',
33396                   '<div class="x-grid-scroller"><div></div></div>',
33397                   '<div class="x-grid-locked">',
33398                       '<div class="x-grid-header">{lockedHeader}</div>',
33399                       '<div class="x-grid-body">{lockedBody}</div>',
33400                   "</div>",
33401                   '<div class="x-grid-viewport">',
33402                       '<div class="x-grid-header">{header}</div>',
33403                       '<div class="x-grid-body">{body}</div>',
33404                   "</div>",
33405                   '<div class="x-grid-bottombar"></div>',
33406                  
33407                   '<div class="x-grid-resize-proxy">&#160;</div>',
33408                "</div>"
33409             );
33410             tpls.master.disableformats = true;
33411         }
33412
33413         if(!tpls.header){
33414             tpls.header = new Roo.Template(
33415                '<table border="0" cellspacing="0" cellpadding="0">',
33416                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
33417                "</table>{splits}"
33418             );
33419             tpls.header.disableformats = true;
33420         }
33421         tpls.header.compile();
33422
33423         if(!tpls.hcell){
33424             tpls.hcell = new Roo.Template(
33425                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
33426                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
33427                 "</div></td>"
33428              );
33429              tpls.hcell.disableFormats = true;
33430         }
33431         tpls.hcell.compile();
33432
33433         if(!tpls.hsplit){
33434             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
33435                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
33436             tpls.hsplit.disableFormats = true;
33437         }
33438         tpls.hsplit.compile();
33439
33440         if(!tpls.body){
33441             tpls.body = new Roo.Template(
33442                '<table border="0" cellspacing="0" cellpadding="0">',
33443                "<tbody>{rows}</tbody>",
33444                "</table>"
33445             );
33446             tpls.body.disableFormats = true;
33447         }
33448         tpls.body.compile();
33449
33450         if(!tpls.row){
33451             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
33452             tpls.row.disableFormats = true;
33453         }
33454         tpls.row.compile();
33455
33456         if(!tpls.cell){
33457             tpls.cell = new Roo.Template(
33458                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
33459                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
33460                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
33461                 "</td>"
33462             );
33463             tpls.cell.disableFormats = true;
33464         }
33465         tpls.cell.compile();
33466
33467         this.templates = tpls;
33468     },
33469
33470     // remap these for backwards compat
33471     onColWidthChange : function(){
33472         this.updateColumns.apply(this, arguments);
33473     },
33474     onHeaderChange : function(){
33475         this.updateHeaders.apply(this, arguments);
33476     }, 
33477     onHiddenChange : function(){
33478         this.handleHiddenChange.apply(this, arguments);
33479     },
33480     onColumnMove : function(){
33481         this.handleColumnMove.apply(this, arguments);
33482     },
33483     onColumnLock : function(){
33484         this.handleLockChange.apply(this, arguments);
33485     },
33486
33487     onDataChange : function(){
33488         this.refresh();
33489         this.updateHeaderSortState();
33490     },
33491
33492     onClear : function(){
33493         this.refresh();
33494     },
33495
33496     onUpdate : function(ds, record){
33497         this.refreshRow(record);
33498     },
33499
33500     refreshRow : function(record){
33501         var ds = this.ds, index;
33502         if(typeof record == 'number'){
33503             index = record;
33504             record = ds.getAt(index);
33505         }else{
33506             index = ds.indexOf(record);
33507         }
33508         this.insertRows(ds, index, index, true);
33509         this.onRemove(ds, record, index+1, true);
33510         this.syncRowHeights(index, index);
33511         this.layout();
33512         this.fireEvent("rowupdated", this, index, record);
33513     },
33514
33515     onAdd : function(ds, records, index){
33516         this.insertRows(ds, index, index + (records.length-1));
33517     },
33518
33519     onRemove : function(ds, record, index, isUpdate){
33520         if(isUpdate !== true){
33521             this.fireEvent("beforerowremoved", this, index, record);
33522         }
33523         var bt = this.getBodyTable(), lt = this.getLockedTable();
33524         if(bt.rows[index]){
33525             bt.firstChild.removeChild(bt.rows[index]);
33526         }
33527         if(lt.rows[index]){
33528             lt.firstChild.removeChild(lt.rows[index]);
33529         }
33530         if(isUpdate !== true){
33531             this.stripeRows(index);
33532             this.syncRowHeights(index, index);
33533             this.layout();
33534             this.fireEvent("rowremoved", this, index, record);
33535         }
33536     },
33537
33538     onLoad : function(){
33539         this.scrollToTop();
33540     },
33541
33542     /**
33543      * Scrolls the grid to the top
33544      */
33545     scrollToTop : function(){
33546         if(this.scroller){
33547             this.scroller.dom.scrollTop = 0;
33548             this.syncScroll();
33549         }
33550     },
33551
33552     /**
33553      * Gets a panel in the header of the grid that can be used for toolbars etc.
33554      * After modifying the contents of this panel a call to grid.autoSize() may be
33555      * required to register any changes in size.
33556      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33557      * @return Roo.Element
33558      */
33559     getHeaderPanel : function(doShow){
33560         if(doShow){
33561             this.headerPanel.show();
33562         }
33563         return this.headerPanel;
33564     },
33565
33566     /**
33567      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33568      * After modifying the contents of this panel a call to grid.autoSize() may be
33569      * required to register any changes in size.
33570      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33571      * @return Roo.Element
33572      */
33573     getFooterPanel : function(doShow){
33574         if(doShow){
33575             this.footerPanel.show();
33576         }
33577         return this.footerPanel;
33578     },
33579
33580     initElements : function(){
33581         var E = Roo.Element;
33582         var el = this.grid.getGridEl().dom.firstChild;
33583         var cs = el.childNodes;
33584
33585         this.el = new E(el);
33586         
33587          this.focusEl = new E(el.firstChild);
33588         this.focusEl.swallowEvent("click", true);
33589         
33590         this.headerPanel = new E(cs[1]);
33591         this.headerPanel.enableDisplayMode("block");
33592
33593         this.scroller = new E(cs[2]);
33594         this.scrollSizer = new E(this.scroller.dom.firstChild);
33595
33596         this.lockedWrap = new E(cs[3]);
33597         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33598         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33599
33600         this.mainWrap = new E(cs[4]);
33601         this.mainHd = new E(this.mainWrap.dom.firstChild);
33602         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33603
33604         this.footerPanel = new E(cs[5]);
33605         this.footerPanel.enableDisplayMode("block");
33606
33607         this.resizeProxy = new E(cs[6]);
33608
33609         this.headerSelector = String.format(
33610            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33611            this.lockedHd.id, this.mainHd.id
33612         );
33613
33614         this.splitterSelector = String.format(
33615            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33616            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33617         );
33618     },
33619     idToCssName : function(s)
33620     {
33621         return s.replace(/[^a-z0-9]+/ig, '-');
33622     },
33623
33624     getHeaderCell : function(index){
33625         return Roo.DomQuery.select(this.headerSelector)[index];
33626     },
33627
33628     getHeaderCellMeasure : function(index){
33629         return this.getHeaderCell(index).firstChild;
33630     },
33631
33632     getHeaderCellText : function(index){
33633         return this.getHeaderCell(index).firstChild.firstChild;
33634     },
33635
33636     getLockedTable : function(){
33637         return this.lockedBody.dom.firstChild;
33638     },
33639
33640     getBodyTable : function(){
33641         return this.mainBody.dom.firstChild;
33642     },
33643
33644     getLockedRow : function(index){
33645         return this.getLockedTable().rows[index];
33646     },
33647
33648     getRow : function(index){
33649         return this.getBodyTable().rows[index];
33650     },
33651
33652     getRowComposite : function(index){
33653         if(!this.rowEl){
33654             this.rowEl = new Roo.CompositeElementLite();
33655         }
33656         var els = [], lrow, mrow;
33657         if(lrow = this.getLockedRow(index)){
33658             els.push(lrow);
33659         }
33660         if(mrow = this.getRow(index)){
33661             els.push(mrow);
33662         }
33663         this.rowEl.elements = els;
33664         return this.rowEl;
33665     },
33666     /**
33667      * Gets the 'td' of the cell
33668      * 
33669      * @param {Integer} rowIndex row to select
33670      * @param {Integer} colIndex column to select
33671      * 
33672      * @return {Object} 
33673      */
33674     getCell : function(rowIndex, colIndex){
33675         var locked = this.cm.getLockedCount();
33676         var source;
33677         if(colIndex < locked){
33678             source = this.lockedBody.dom.firstChild;
33679         }else{
33680             source = this.mainBody.dom.firstChild;
33681             colIndex -= locked;
33682         }
33683         return source.rows[rowIndex].childNodes[colIndex];
33684     },
33685
33686     getCellText : function(rowIndex, colIndex){
33687         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33688     },
33689
33690     getCellBox : function(cell){
33691         var b = this.fly(cell).getBox();
33692         if(Roo.isOpera){ // opera fails to report the Y
33693             b.y = cell.offsetTop + this.mainBody.getY();
33694         }
33695         return b;
33696     },
33697
33698     getCellIndex : function(cell){
33699         var id = String(cell.className).match(this.cellRE);
33700         if(id){
33701             return parseInt(id[1], 10);
33702         }
33703         return 0;
33704     },
33705
33706     findHeaderIndex : function(n){
33707         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33708         return r ? this.getCellIndex(r) : false;
33709     },
33710
33711     findHeaderCell : function(n){
33712         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33713         return r ? r : false;
33714     },
33715
33716     findRowIndex : function(n){
33717         if(!n){
33718             return false;
33719         }
33720         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33721         return r ? r.rowIndex : false;
33722     },
33723
33724     findCellIndex : function(node){
33725         var stop = this.el.dom;
33726         while(node && node != stop){
33727             if(this.findRE.test(node.className)){
33728                 return this.getCellIndex(node);
33729             }
33730             node = node.parentNode;
33731         }
33732         return false;
33733     },
33734
33735     getColumnId : function(index){
33736         return this.cm.getColumnId(index);
33737     },
33738
33739     getSplitters : function()
33740     {
33741         if(this.splitterSelector){
33742            return Roo.DomQuery.select(this.splitterSelector);
33743         }else{
33744             return null;
33745       }
33746     },
33747
33748     getSplitter : function(index){
33749         return this.getSplitters()[index];
33750     },
33751
33752     onRowOver : function(e, t){
33753         var row;
33754         if((row = this.findRowIndex(t)) !== false){
33755             this.getRowComposite(row).addClass("x-grid-row-over");
33756         }
33757     },
33758
33759     onRowOut : function(e, t){
33760         var row;
33761         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33762             this.getRowComposite(row).removeClass("x-grid-row-over");
33763         }
33764     },
33765
33766     renderHeaders : function(){
33767         var cm = this.cm;
33768         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33769         var cb = [], lb = [], sb = [], lsb = [], p = {};
33770         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33771             p.cellId = "x-grid-hd-0-" + i;
33772             p.splitId = "x-grid-csplit-0-" + i;
33773             p.id = cm.getColumnId(i);
33774             p.value = cm.getColumnHeader(i) || "";
33775             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
33776             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33777             if(!cm.isLocked(i)){
33778                 cb[cb.length] = ct.apply(p);
33779                 sb[sb.length] = st.apply(p);
33780             }else{
33781                 lb[lb.length] = ct.apply(p);
33782                 lsb[lsb.length] = st.apply(p);
33783             }
33784         }
33785         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33786                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33787     },
33788
33789     updateHeaders : function(){
33790         var html = this.renderHeaders();
33791         this.lockedHd.update(html[0]);
33792         this.mainHd.update(html[1]);
33793     },
33794
33795     /**
33796      * Focuses the specified row.
33797      * @param {Number} row The row index
33798      */
33799     focusRow : function(row)
33800     {
33801         //Roo.log('GridView.focusRow');
33802         var x = this.scroller.dom.scrollLeft;
33803         this.focusCell(row, 0, false);
33804         this.scroller.dom.scrollLeft = x;
33805     },
33806
33807     /**
33808      * Focuses the specified cell.
33809      * @param {Number} row The row index
33810      * @param {Number} col The column index
33811      * @param {Boolean} hscroll false to disable horizontal scrolling
33812      */
33813     focusCell : function(row, col, hscroll)
33814     {
33815         //Roo.log('GridView.focusCell');
33816         var el = this.ensureVisible(row, col, hscroll);
33817         this.focusEl.alignTo(el, "tl-tl");
33818         if(Roo.isGecko){
33819             this.focusEl.focus();
33820         }else{
33821             this.focusEl.focus.defer(1, this.focusEl);
33822         }
33823     },
33824
33825     /**
33826      * Scrolls the specified cell into view
33827      * @param {Number} row The row index
33828      * @param {Number} col The column index
33829      * @param {Boolean} hscroll false to disable horizontal scrolling
33830      */
33831     ensureVisible : function(row, col, hscroll)
33832     {
33833         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33834         //return null; //disable for testing.
33835         if(typeof row != "number"){
33836             row = row.rowIndex;
33837         }
33838         if(row < 0 && row >= this.ds.getCount()){
33839             return  null;
33840         }
33841         col = (col !== undefined ? col : 0);
33842         var cm = this.grid.colModel;
33843         while(cm.isHidden(col)){
33844             col++;
33845         }
33846
33847         var el = this.getCell(row, col);
33848         if(!el){
33849             return null;
33850         }
33851         var c = this.scroller.dom;
33852
33853         var ctop = parseInt(el.offsetTop, 10);
33854         var cleft = parseInt(el.offsetLeft, 10);
33855         var cbot = ctop + el.offsetHeight;
33856         var cright = cleft + el.offsetWidth;
33857         
33858         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33859         var stop = parseInt(c.scrollTop, 10);
33860         var sleft = parseInt(c.scrollLeft, 10);
33861         var sbot = stop + ch;
33862         var sright = sleft + c.clientWidth;
33863         /*
33864         Roo.log('GridView.ensureVisible:' +
33865                 ' ctop:' + ctop +
33866                 ' c.clientHeight:' + c.clientHeight +
33867                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33868                 ' stop:' + stop +
33869                 ' cbot:' + cbot +
33870                 ' sbot:' + sbot +
33871                 ' ch:' + ch  
33872                 );
33873         */
33874         if(ctop < stop){
33875             c.scrollTop = ctop;
33876             //Roo.log("set scrolltop to ctop DISABLE?");
33877         }else if(cbot > sbot){
33878             //Roo.log("set scrolltop to cbot-ch");
33879             c.scrollTop = cbot-ch;
33880         }
33881         
33882         if(hscroll !== false){
33883             if(cleft < sleft){
33884                 c.scrollLeft = cleft;
33885             }else if(cright > sright){
33886                 c.scrollLeft = cright-c.clientWidth;
33887             }
33888         }
33889          
33890         return el;
33891     },
33892
33893     updateColumns : function(){
33894         this.grid.stopEditing();
33895         var cm = this.grid.colModel, colIds = this.getColumnIds();
33896         //var totalWidth = cm.getTotalWidth();
33897         var pos = 0;
33898         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33899             //if(cm.isHidden(i)) continue;
33900             var w = cm.getColumnWidth(i);
33901             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33902             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33903         }
33904         this.updateSplitters();
33905     },
33906
33907     generateRules : function(cm){
33908         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33909         Roo.util.CSS.removeStyleSheet(rulesId);
33910         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33911             var cid = cm.getColumnId(i);
33912             var align = '';
33913             if(cm.config[i].align){
33914                 align = 'text-align:'+cm.config[i].align+';';
33915             }
33916             var hidden = '';
33917             if(cm.isHidden(i)){
33918                 hidden = 'display:none;';
33919             }
33920             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33921             ruleBuf.push(
33922                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33923                     this.hdSelector, cid, " {\n", align, width, "}\n",
33924                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33925                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33926         }
33927         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33928     },
33929
33930     updateSplitters : function(){
33931         var cm = this.cm, s = this.getSplitters();
33932         if(s){ // splitters not created yet
33933             var pos = 0, locked = true;
33934             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33935                 if(cm.isHidden(i)) {
33936                     continue;
33937                 }
33938                 var w = cm.getColumnWidth(i); // make sure it's a number
33939                 if(!cm.isLocked(i) && locked){
33940                     pos = 0;
33941                     locked = false;
33942                 }
33943                 pos += w;
33944                 s[i].style.left = (pos-this.splitOffset) + "px";
33945             }
33946         }
33947     },
33948
33949     handleHiddenChange : function(colModel, colIndex, hidden){
33950         if(hidden){
33951             this.hideColumn(colIndex);
33952         }else{
33953             this.unhideColumn(colIndex);
33954         }
33955     },
33956
33957     hideColumn : function(colIndex){
33958         var cid = this.getColumnId(colIndex);
33959         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33960         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33961         if(Roo.isSafari){
33962             this.updateHeaders();
33963         }
33964         this.updateSplitters();
33965         this.layout();
33966     },
33967
33968     unhideColumn : function(colIndex){
33969         var cid = this.getColumnId(colIndex);
33970         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33971         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33972
33973         if(Roo.isSafari){
33974             this.updateHeaders();
33975         }
33976         this.updateSplitters();
33977         this.layout();
33978     },
33979
33980     insertRows : function(dm, firstRow, lastRow, isUpdate){
33981         if(firstRow == 0 && lastRow == dm.getCount()-1){
33982             this.refresh();
33983         }else{
33984             if(!isUpdate){
33985                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33986             }
33987             var s = this.getScrollState();
33988             var markup = this.renderRows(firstRow, lastRow);
33989             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33990             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33991             this.restoreScroll(s);
33992             if(!isUpdate){
33993                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33994                 this.syncRowHeights(firstRow, lastRow);
33995                 this.stripeRows(firstRow);
33996                 this.layout();
33997             }
33998         }
33999     },
34000
34001     bufferRows : function(markup, target, index){
34002         var before = null, trows = target.rows, tbody = target.tBodies[0];
34003         if(index < trows.length){
34004             before = trows[index];
34005         }
34006         var b = document.createElement("div");
34007         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
34008         var rows = b.firstChild.rows;
34009         for(var i = 0, len = rows.length; i < len; i++){
34010             if(before){
34011                 tbody.insertBefore(rows[0], before);
34012             }else{
34013                 tbody.appendChild(rows[0]);
34014             }
34015         }
34016         b.innerHTML = "";
34017         b = null;
34018     },
34019
34020     deleteRows : function(dm, firstRow, lastRow){
34021         if(dm.getRowCount()<1){
34022             this.fireEvent("beforerefresh", this);
34023             this.mainBody.update("");
34024             this.lockedBody.update("");
34025             this.fireEvent("refresh", this);
34026         }else{
34027             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
34028             var bt = this.getBodyTable();
34029             var tbody = bt.firstChild;
34030             var rows = bt.rows;
34031             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
34032                 tbody.removeChild(rows[firstRow]);
34033             }
34034             this.stripeRows(firstRow);
34035             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
34036         }
34037     },
34038
34039     updateRows : function(dataSource, firstRow, lastRow){
34040         var s = this.getScrollState();
34041         this.refresh();
34042         this.restoreScroll(s);
34043     },
34044
34045     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
34046         if(!noRefresh){
34047            this.refresh();
34048         }
34049         this.updateHeaderSortState();
34050     },
34051
34052     getScrollState : function(){
34053         
34054         var sb = this.scroller.dom;
34055         return {left: sb.scrollLeft, top: sb.scrollTop};
34056     },
34057
34058     stripeRows : function(startRow){
34059         if(!this.grid.stripeRows || this.ds.getCount() < 1){
34060             return;
34061         }
34062         startRow = startRow || 0;
34063         var rows = this.getBodyTable().rows;
34064         var lrows = this.getLockedTable().rows;
34065         var cls = ' x-grid-row-alt ';
34066         for(var i = startRow, len = rows.length; i < len; i++){
34067             var row = rows[i], lrow = lrows[i];
34068             var isAlt = ((i+1) % 2 == 0);
34069             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
34070             if(isAlt == hasAlt){
34071                 continue;
34072             }
34073             if(isAlt){
34074                 row.className += " x-grid-row-alt";
34075             }else{
34076                 row.className = row.className.replace("x-grid-row-alt", "");
34077             }
34078             if(lrow){
34079                 lrow.className = row.className;
34080             }
34081         }
34082     },
34083
34084     restoreScroll : function(state){
34085         //Roo.log('GridView.restoreScroll');
34086         var sb = this.scroller.dom;
34087         sb.scrollLeft = state.left;
34088         sb.scrollTop = state.top;
34089         this.syncScroll();
34090     },
34091
34092     syncScroll : function(){
34093         //Roo.log('GridView.syncScroll');
34094         var sb = this.scroller.dom;
34095         var sh = this.mainHd.dom;
34096         var bs = this.mainBody.dom;
34097         var lv = this.lockedBody.dom;
34098         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
34099         lv.scrollTop = bs.scrollTop = sb.scrollTop;
34100     },
34101
34102     handleScroll : function(e){
34103         this.syncScroll();
34104         var sb = this.scroller.dom;
34105         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
34106         e.stopEvent();
34107     },
34108
34109     handleWheel : function(e){
34110         var d = e.getWheelDelta();
34111         this.scroller.dom.scrollTop -= d*22;
34112         // set this here to prevent jumpy scrolling on large tables
34113         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
34114         e.stopEvent();
34115     },
34116
34117     renderRows : function(startRow, endRow){
34118         // pull in all the crap needed to render rows
34119         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
34120         var colCount = cm.getColumnCount();
34121
34122         if(ds.getCount() < 1){
34123             return ["", ""];
34124         }
34125
34126         // build a map for all the columns
34127         var cs = [];
34128         for(var i = 0; i < colCount; i++){
34129             var name = cm.getDataIndex(i);
34130             cs[i] = {
34131                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
34132                 renderer : cm.getRenderer(i),
34133                 id : cm.getColumnId(i),
34134                 locked : cm.isLocked(i),
34135                 has_editor : cm.isCellEditable(i)
34136             };
34137         }
34138
34139         startRow = startRow || 0;
34140         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
34141
34142         // records to render
34143         var rs = ds.getRange(startRow, endRow);
34144
34145         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
34146     },
34147
34148     // As much as I hate to duplicate code, this was branched because FireFox really hates
34149     // [].join("") on strings. The performance difference was substantial enough to
34150     // branch this function
34151     doRender : Roo.isGecko ?
34152             function(cs, rs, ds, startRow, colCount, stripe){
34153                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34154                 // buffers
34155                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34156                 
34157                 var hasListener = this.grid.hasListener('rowclass');
34158                 var rowcfg = {};
34159                 for(var j = 0, len = rs.length; j < len; j++){
34160                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34161                     for(var i = 0; i < colCount; i++){
34162                         c = cs[i];
34163                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34164                         p.id = c.id;
34165                         p.css = p.attr = "";
34166                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34167                         if(p.value == undefined || p.value === "") {
34168                             p.value = "&#160;";
34169                         }
34170                         if(c.has_editor){
34171                             p.css += ' x-grid-editable-cell';
34172                         }
34173                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
34174                             p.css +=  ' x-grid-dirty-cell';
34175                         }
34176                         var markup = ct.apply(p);
34177                         if(!c.locked){
34178                             cb+= markup;
34179                         }else{
34180                             lcb+= markup;
34181                         }
34182                     }
34183                     var alt = [];
34184                     if(stripe && ((rowIndex+1) % 2 == 0)){
34185                         alt.push("x-grid-row-alt")
34186                     }
34187                     if(r.dirty){
34188                         alt.push(  " x-grid-dirty-row");
34189                     }
34190                     rp.cells = lcb;
34191                     if(this.getRowClass){
34192                         alt.push(this.getRowClass(r, rowIndex));
34193                     }
34194                     if (hasListener) {
34195                         rowcfg = {
34196                              
34197                             record: r,
34198                             rowIndex : rowIndex,
34199                             rowClass : ''
34200                         };
34201                         this.grid.fireEvent('rowclass', this, rowcfg);
34202                         alt.push(rowcfg.rowClass);
34203                     }
34204                     rp.alt = alt.join(" ");
34205                     lbuf+= rt.apply(rp);
34206                     rp.cells = cb;
34207                     buf+=  rt.apply(rp);
34208                 }
34209                 return [lbuf, buf];
34210             } :
34211             function(cs, rs, ds, startRow, colCount, stripe){
34212                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34213                 // buffers
34214                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34215                 var hasListener = this.grid.hasListener('rowclass');
34216  
34217                 var rowcfg = {};
34218                 for(var j = 0, len = rs.length; j < len; j++){
34219                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
34220                     for(var i = 0; i < colCount; i++){
34221                         c = cs[i];
34222                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34223                         p.id = c.id;
34224                         p.css = p.attr = "";
34225                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34226                         if(p.value == undefined || p.value === "") {
34227                             p.value = "&#160;";
34228                         }
34229                         //Roo.log(c);
34230                          if(c.has_editor){
34231                             p.css += ' x-grid-editable-cell';
34232                         }
34233                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34234                             p.css += ' x-grid-dirty-cell' 
34235                         }
34236                         
34237                         var markup = ct.apply(p);
34238                         if(!c.locked){
34239                             cb[cb.length] = markup;
34240                         }else{
34241                             lcb[lcb.length] = markup;
34242                         }
34243                     }
34244                     var alt = [];
34245                     if(stripe && ((rowIndex+1) % 2 == 0)){
34246                         alt.push( "x-grid-row-alt");
34247                     }
34248                     if(r.dirty){
34249                         alt.push(" x-grid-dirty-row");
34250                     }
34251                     rp.cells = lcb;
34252                     if(this.getRowClass){
34253                         alt.push( this.getRowClass(r, rowIndex));
34254                     }
34255                     if (hasListener) {
34256                         rowcfg = {
34257                              
34258                             record: r,
34259                             rowIndex : rowIndex,
34260                             rowClass : ''
34261                         };
34262                         this.grid.fireEvent('rowclass', this, rowcfg);
34263                         alt.push(rowcfg.rowClass);
34264                     }
34265                     
34266                     rp.alt = alt.join(" ");
34267                     rp.cells = lcb.join("");
34268                     lbuf[lbuf.length] = rt.apply(rp);
34269                     rp.cells = cb.join("");
34270                     buf[buf.length] =  rt.apply(rp);
34271                 }
34272                 return [lbuf.join(""), buf.join("")];
34273             },
34274
34275     renderBody : function(){
34276         var markup = this.renderRows();
34277         var bt = this.templates.body;
34278         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34279     },
34280
34281     /**
34282      * Refreshes the grid
34283      * @param {Boolean} headersToo
34284      */
34285     refresh : function(headersToo){
34286         this.fireEvent("beforerefresh", this);
34287         this.grid.stopEditing();
34288         var result = this.renderBody();
34289         this.lockedBody.update(result[0]);
34290         this.mainBody.update(result[1]);
34291         if(headersToo === true){
34292             this.updateHeaders();
34293             this.updateColumns();
34294             this.updateSplitters();
34295             this.updateHeaderSortState();
34296         }
34297         this.syncRowHeights();
34298         this.layout();
34299         this.fireEvent("refresh", this);
34300     },
34301
34302     handleColumnMove : function(cm, oldIndex, newIndex){
34303         this.indexMap = null;
34304         var s = this.getScrollState();
34305         this.refresh(true);
34306         this.restoreScroll(s);
34307         this.afterMove(newIndex);
34308     },
34309
34310     afterMove : function(colIndex){
34311         if(this.enableMoveAnim && Roo.enableFx){
34312             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34313         }
34314         // if multisort - fix sortOrder, and reload..
34315         if (this.grid.dataSource.multiSort) {
34316             // the we can call sort again..
34317             var dm = this.grid.dataSource;
34318             var cm = this.grid.colModel;
34319             var so = [];
34320             for(var i = 0; i < cm.config.length; i++ ) {
34321                 
34322                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34323                     continue; // dont' bother, it's not in sort list or being set.
34324                 }
34325                 
34326                 so.push(cm.config[i].dataIndex);
34327             };
34328             dm.sortOrder = so;
34329             dm.load(dm.lastOptions);
34330             
34331             
34332         }
34333         
34334     },
34335
34336     updateCell : function(dm, rowIndex, dataIndex){
34337         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34338         if(typeof colIndex == "undefined"){ // not present in grid
34339             return;
34340         }
34341         var cm = this.grid.colModel;
34342         var cell = this.getCell(rowIndex, colIndex);
34343         var cellText = this.getCellText(rowIndex, colIndex);
34344
34345         var p = {
34346             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34347             id : cm.getColumnId(colIndex),
34348             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34349         };
34350         var renderer = cm.getRenderer(colIndex);
34351         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34352         if(typeof val == "undefined" || val === "") {
34353             val = "&#160;";
34354         }
34355         cellText.innerHTML = val;
34356         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34357         this.syncRowHeights(rowIndex, rowIndex);
34358     },
34359
34360     calcColumnWidth : function(colIndex, maxRowsToMeasure){
34361         var maxWidth = 0;
34362         if(this.grid.autoSizeHeaders){
34363             var h = this.getHeaderCellMeasure(colIndex);
34364             maxWidth = Math.max(maxWidth, h.scrollWidth);
34365         }
34366         var tb, index;
34367         if(this.cm.isLocked(colIndex)){
34368             tb = this.getLockedTable();
34369             index = colIndex;
34370         }else{
34371             tb = this.getBodyTable();
34372             index = colIndex - this.cm.getLockedCount();
34373         }
34374         if(tb && tb.rows){
34375             var rows = tb.rows;
34376             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34377             for(var i = 0; i < stopIndex; i++){
34378                 var cell = rows[i].childNodes[index].firstChild;
34379                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34380             }
34381         }
34382         return maxWidth + /*margin for error in IE*/ 5;
34383     },
34384     /**
34385      * Autofit a column to its content.
34386      * @param {Number} colIndex
34387      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34388      */
34389      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34390          if(this.cm.isHidden(colIndex)){
34391              return; // can't calc a hidden column
34392          }
34393         if(forceMinSize){
34394             var cid = this.cm.getColumnId(colIndex);
34395             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34396            if(this.grid.autoSizeHeaders){
34397                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34398            }
34399         }
34400         var newWidth = this.calcColumnWidth(colIndex);
34401         this.cm.setColumnWidth(colIndex,
34402             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34403         if(!suppressEvent){
34404             this.grid.fireEvent("columnresize", colIndex, newWidth);
34405         }
34406     },
34407
34408     /**
34409      * Autofits all columns to their content and then expands to fit any extra space in the grid
34410      */
34411      autoSizeColumns : function(){
34412         var cm = this.grid.colModel;
34413         var colCount = cm.getColumnCount();
34414         for(var i = 0; i < colCount; i++){
34415             this.autoSizeColumn(i, true, true);
34416         }
34417         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
34418             this.fitColumns();
34419         }else{
34420             this.updateColumns();
34421             this.layout();
34422         }
34423     },
34424
34425     /**
34426      * Autofits all columns to the grid's width proportionate with their current size
34427      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
34428      */
34429     fitColumns : function(reserveScrollSpace){
34430         var cm = this.grid.colModel;
34431         var colCount = cm.getColumnCount();
34432         var cols = [];
34433         var width = 0;
34434         var i, w;
34435         for (i = 0; i < colCount; i++){
34436             if(!cm.isHidden(i) && !cm.isFixed(i)){
34437                 w = cm.getColumnWidth(i);
34438                 cols.push(i);
34439                 cols.push(w);
34440                 width += w;
34441             }
34442         }
34443         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
34444         if(reserveScrollSpace){
34445             avail -= 17;
34446         }
34447         var frac = (avail - cm.getTotalWidth())/width;
34448         while (cols.length){
34449             w = cols.pop();
34450             i = cols.pop();
34451             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
34452         }
34453         this.updateColumns();
34454         this.layout();
34455     },
34456
34457     onRowSelect : function(rowIndex){
34458         var row = this.getRowComposite(rowIndex);
34459         row.addClass("x-grid-row-selected");
34460     },
34461
34462     onRowDeselect : function(rowIndex){
34463         var row = this.getRowComposite(rowIndex);
34464         row.removeClass("x-grid-row-selected");
34465     },
34466
34467     onCellSelect : function(row, col){
34468         var cell = this.getCell(row, col);
34469         if(cell){
34470             Roo.fly(cell).addClass("x-grid-cell-selected");
34471         }
34472     },
34473
34474     onCellDeselect : function(row, col){
34475         var cell = this.getCell(row, col);
34476         if(cell){
34477             Roo.fly(cell).removeClass("x-grid-cell-selected");
34478         }
34479     },
34480
34481     updateHeaderSortState : function(){
34482         
34483         // sort state can be single { field: xxx, direction : yyy}
34484         // or   { xxx=>ASC , yyy : DESC ..... }
34485         
34486         var mstate = {};
34487         if (!this.ds.multiSort) { 
34488             var state = this.ds.getSortState();
34489             if(!state){
34490                 return;
34491             }
34492             mstate[state.field] = state.direction;
34493             // FIXME... - this is not used here.. but might be elsewhere..
34494             this.sortState = state;
34495             
34496         } else {
34497             mstate = this.ds.sortToggle;
34498         }
34499         //remove existing sort classes..
34500         
34501         var sc = this.sortClasses;
34502         var hds = this.el.select(this.headerSelector).removeClass(sc);
34503         
34504         for(var f in mstate) {
34505         
34506             var sortColumn = this.cm.findColumnIndex(f);
34507             
34508             if(sortColumn != -1){
34509                 var sortDir = mstate[f];        
34510                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34511             }
34512         }
34513         
34514          
34515         
34516     },
34517
34518
34519     handleHeaderClick : function(g, index,e){
34520         
34521         Roo.log("header click");
34522         
34523         if (Roo.isTouch) {
34524             // touch events on header are handled by context
34525             this.handleHdCtx(g,index,e);
34526             return;
34527         }
34528         
34529         
34530         if(this.headersDisabled){
34531             return;
34532         }
34533         var dm = g.dataSource, cm = g.colModel;
34534         if(!cm.isSortable(index)){
34535             return;
34536         }
34537         g.stopEditing();
34538         
34539         if (dm.multiSort) {
34540             // update the sortOrder
34541             var so = [];
34542             for(var i = 0; i < cm.config.length; i++ ) {
34543                 
34544                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34545                     continue; // dont' bother, it's not in sort list or being set.
34546                 }
34547                 
34548                 so.push(cm.config[i].dataIndex);
34549             };
34550             dm.sortOrder = so;
34551         }
34552         
34553         
34554         dm.sort(cm.getDataIndex(index));
34555     },
34556
34557
34558     destroy : function(){
34559         if(this.colMenu){
34560             this.colMenu.removeAll();
34561             Roo.menu.MenuMgr.unregister(this.colMenu);
34562             this.colMenu.getEl().remove();
34563             delete this.colMenu;
34564         }
34565         if(this.hmenu){
34566             this.hmenu.removeAll();
34567             Roo.menu.MenuMgr.unregister(this.hmenu);
34568             this.hmenu.getEl().remove();
34569             delete this.hmenu;
34570         }
34571         if(this.grid.enableColumnMove){
34572             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34573             if(dds){
34574                 for(var dd in dds){
34575                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34576                         var elid = dds[dd].dragElId;
34577                         dds[dd].unreg();
34578                         Roo.get(elid).remove();
34579                     } else if(dds[dd].config.isTarget){
34580                         dds[dd].proxyTop.remove();
34581                         dds[dd].proxyBottom.remove();
34582                         dds[dd].unreg();
34583                     }
34584                     if(Roo.dd.DDM.locationCache[dd]){
34585                         delete Roo.dd.DDM.locationCache[dd];
34586                     }
34587                 }
34588                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34589             }
34590         }
34591         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34592         this.bind(null, null);
34593         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34594     },
34595
34596     handleLockChange : function(){
34597         this.refresh(true);
34598     },
34599
34600     onDenyColumnLock : function(){
34601
34602     },
34603
34604     onDenyColumnHide : function(){
34605
34606     },
34607
34608     handleHdMenuClick : function(item){
34609         var index = this.hdCtxIndex;
34610         var cm = this.cm, ds = this.ds;
34611         switch(item.id){
34612             case "asc":
34613                 ds.sort(cm.getDataIndex(index), "ASC");
34614                 break;
34615             case "desc":
34616                 ds.sort(cm.getDataIndex(index), "DESC");
34617                 break;
34618             case "lock":
34619                 var lc = cm.getLockedCount();
34620                 if(cm.getColumnCount(true) <= lc+1){
34621                     this.onDenyColumnLock();
34622                     return;
34623                 }
34624                 if(lc != index){
34625                     cm.setLocked(index, true, true);
34626                     cm.moveColumn(index, lc);
34627                     this.grid.fireEvent("columnmove", index, lc);
34628                 }else{
34629                     cm.setLocked(index, true);
34630                 }
34631             break;
34632             case "unlock":
34633                 var lc = cm.getLockedCount();
34634                 if((lc-1) != index){
34635                     cm.setLocked(index, false, true);
34636                     cm.moveColumn(index, lc-1);
34637                     this.grid.fireEvent("columnmove", index, lc-1);
34638                 }else{
34639                     cm.setLocked(index, false);
34640                 }
34641             break;
34642             case 'wider': // used to expand cols on touch..
34643             case 'narrow':
34644                 var cw = cm.getColumnWidth(index);
34645                 cw += (item.id == 'wider' ? 1 : -1) * 50;
34646                 cw = Math.max(0, cw);
34647                 cw = Math.min(cw,4000);
34648                 cm.setColumnWidth(index, cw);
34649                 break;
34650                 
34651             default:
34652                 index = cm.getIndexById(item.id.substr(4));
34653                 if(index != -1){
34654                     if(item.checked && cm.getColumnCount(true) <= 1){
34655                         this.onDenyColumnHide();
34656                         return false;
34657                     }
34658                     cm.setHidden(index, item.checked);
34659                 }
34660         }
34661         return true;
34662     },
34663
34664     beforeColMenuShow : function(){
34665         var cm = this.cm,  colCount = cm.getColumnCount();
34666         this.colMenu.removeAll();
34667         for(var i = 0; i < colCount; i++){
34668             this.colMenu.add(new Roo.menu.CheckItem({
34669                 id: "col-"+cm.getColumnId(i),
34670                 text: cm.getColumnHeader(i),
34671                 checked: !cm.isHidden(i),
34672                 hideOnClick:false
34673             }));
34674         }
34675     },
34676
34677     handleHdCtx : function(g, index, e){
34678         e.stopEvent();
34679         var hd = this.getHeaderCell(index);
34680         this.hdCtxIndex = index;
34681         var ms = this.hmenu.items, cm = this.cm;
34682         ms.get("asc").setDisabled(!cm.isSortable(index));
34683         ms.get("desc").setDisabled(!cm.isSortable(index));
34684         if(this.grid.enableColLock !== false){
34685             ms.get("lock").setDisabled(cm.isLocked(index));
34686             ms.get("unlock").setDisabled(!cm.isLocked(index));
34687         }
34688         this.hmenu.show(hd, "tl-bl");
34689     },
34690
34691     handleHdOver : function(e){
34692         var hd = this.findHeaderCell(e.getTarget());
34693         if(hd && !this.headersDisabled){
34694             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34695                this.fly(hd).addClass("x-grid-hd-over");
34696             }
34697         }
34698     },
34699
34700     handleHdOut : function(e){
34701         var hd = this.findHeaderCell(e.getTarget());
34702         if(hd){
34703             this.fly(hd).removeClass("x-grid-hd-over");
34704         }
34705     },
34706
34707     handleSplitDblClick : function(e, t){
34708         var i = this.getCellIndex(t);
34709         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34710             this.autoSizeColumn(i, true);
34711             this.layout();
34712         }
34713     },
34714
34715     render : function(){
34716
34717         var cm = this.cm;
34718         var colCount = cm.getColumnCount();
34719
34720         if(this.grid.monitorWindowResize === true){
34721             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34722         }
34723         var header = this.renderHeaders();
34724         var body = this.templates.body.apply({rows:""});
34725         var html = this.templates.master.apply({
34726             lockedBody: body,
34727             body: body,
34728             lockedHeader: header[0],
34729             header: header[1]
34730         });
34731
34732         //this.updateColumns();
34733
34734         this.grid.getGridEl().dom.innerHTML = html;
34735
34736         this.initElements();
34737         
34738         // a kludge to fix the random scolling effect in webkit
34739         this.el.on("scroll", function() {
34740             this.el.dom.scrollTop=0; // hopefully not recursive..
34741         },this);
34742
34743         this.scroller.on("scroll", this.handleScroll, this);
34744         this.lockedBody.on("mousewheel", this.handleWheel, this);
34745         this.mainBody.on("mousewheel", this.handleWheel, this);
34746
34747         this.mainHd.on("mouseover", this.handleHdOver, this);
34748         this.mainHd.on("mouseout", this.handleHdOut, this);
34749         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34750                 {delegate: "."+this.splitClass});
34751
34752         this.lockedHd.on("mouseover", this.handleHdOver, this);
34753         this.lockedHd.on("mouseout", this.handleHdOut, this);
34754         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34755                 {delegate: "."+this.splitClass});
34756
34757         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34758             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34759         }
34760
34761         this.updateSplitters();
34762
34763         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34764             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34765             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34766         }
34767
34768         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34769             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34770             this.hmenu.add(
34771                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34772                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34773             );
34774             if(this.grid.enableColLock !== false){
34775                 this.hmenu.add('-',
34776                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34777                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34778                 );
34779             }
34780             if (Roo.isTouch) {
34781                  this.hmenu.add('-',
34782                     {id:"wider", text: this.columnsWiderText},
34783                     {id:"narrow", text: this.columnsNarrowText }
34784                 );
34785                 
34786                  
34787             }
34788             
34789             if(this.grid.enableColumnHide !== false){
34790
34791                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34792                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34793                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34794
34795                 this.hmenu.add('-',
34796                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34797                 );
34798             }
34799             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34800
34801             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34802         }
34803
34804         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34805             this.dd = new Roo.grid.GridDragZone(this.grid, {
34806                 ddGroup : this.grid.ddGroup || 'GridDD'
34807             });
34808             
34809         }
34810
34811         /*
34812         for(var i = 0; i < colCount; i++){
34813             if(cm.isHidden(i)){
34814                 this.hideColumn(i);
34815             }
34816             if(cm.config[i].align){
34817                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34818                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34819             }
34820         }*/
34821         
34822         this.updateHeaderSortState();
34823
34824         this.beforeInitialResize();
34825         this.layout(true);
34826
34827         // two part rendering gives faster view to the user
34828         this.renderPhase2.defer(1, this);
34829     },
34830
34831     renderPhase2 : function(){
34832         // render the rows now
34833         this.refresh();
34834         if(this.grid.autoSizeColumns){
34835             this.autoSizeColumns();
34836         }
34837     },
34838
34839     beforeInitialResize : function(){
34840
34841     },
34842
34843     onColumnSplitterMoved : function(i, w){
34844         this.userResized = true;
34845         var cm = this.grid.colModel;
34846         cm.setColumnWidth(i, w, true);
34847         var cid = cm.getColumnId(i);
34848         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34849         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34850         this.updateSplitters();
34851         this.layout();
34852         this.grid.fireEvent("columnresize", i, w);
34853     },
34854
34855     syncRowHeights : function(startIndex, endIndex){
34856         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34857             startIndex = startIndex || 0;
34858             var mrows = this.getBodyTable().rows;
34859             var lrows = this.getLockedTable().rows;
34860             var len = mrows.length-1;
34861             endIndex = Math.min(endIndex || len, len);
34862             for(var i = startIndex; i <= endIndex; i++){
34863                 var m = mrows[i], l = lrows[i];
34864                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34865                 m.style.height = l.style.height = h + "px";
34866             }
34867         }
34868     },
34869
34870     layout : function(initialRender, is2ndPass)
34871     {
34872         var g = this.grid;
34873         var auto = g.autoHeight;
34874         var scrollOffset = 16;
34875         var c = g.getGridEl(), cm = this.cm,
34876                 expandCol = g.autoExpandColumn,
34877                 gv = this;
34878         //c.beginMeasure();
34879
34880         if(!c.dom.offsetWidth){ // display:none?
34881             if(initialRender){
34882                 this.lockedWrap.show();
34883                 this.mainWrap.show();
34884             }
34885             return;
34886         }
34887
34888         var hasLock = this.cm.isLocked(0);
34889
34890         var tbh = this.headerPanel.getHeight();
34891         var bbh = this.footerPanel.getHeight();
34892
34893         if(auto){
34894             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34895             var newHeight = ch + c.getBorderWidth("tb");
34896             if(g.maxHeight){
34897                 newHeight = Math.min(g.maxHeight, newHeight);
34898             }
34899             c.setHeight(newHeight);
34900         }
34901
34902         if(g.autoWidth){
34903             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34904         }
34905
34906         var s = this.scroller;
34907
34908         var csize = c.getSize(true);
34909
34910         this.el.setSize(csize.width, csize.height);
34911
34912         this.headerPanel.setWidth(csize.width);
34913         this.footerPanel.setWidth(csize.width);
34914
34915         var hdHeight = this.mainHd.getHeight();
34916         var vw = csize.width;
34917         var vh = csize.height - (tbh + bbh);
34918
34919         s.setSize(vw, vh);
34920
34921         var bt = this.getBodyTable();
34922         
34923         if(cm.getLockedCount() == cm.config.length){
34924             bt = this.getLockedTable();
34925         }
34926         
34927         var ltWidth = hasLock ?
34928                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34929
34930         var scrollHeight = bt.offsetHeight;
34931         var scrollWidth = ltWidth + bt.offsetWidth;
34932         var vscroll = false, hscroll = false;
34933
34934         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34935
34936         var lw = this.lockedWrap, mw = this.mainWrap;
34937         var lb = this.lockedBody, mb = this.mainBody;
34938
34939         setTimeout(function(){
34940             var t = s.dom.offsetTop;
34941             var w = s.dom.clientWidth,
34942                 h = s.dom.clientHeight;
34943
34944             lw.setTop(t);
34945             lw.setSize(ltWidth, h);
34946
34947             mw.setLeftTop(ltWidth, t);
34948             mw.setSize(w-ltWidth, h);
34949
34950             lb.setHeight(h-hdHeight);
34951             mb.setHeight(h-hdHeight);
34952
34953             if(is2ndPass !== true && !gv.userResized && expandCol){
34954                 // high speed resize without full column calculation
34955                 
34956                 var ci = cm.getIndexById(expandCol);
34957                 if (ci < 0) {
34958                     ci = cm.findColumnIndex(expandCol);
34959                 }
34960                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34961                 var expandId = cm.getColumnId(ci);
34962                 var  tw = cm.getTotalWidth(false);
34963                 var currentWidth = cm.getColumnWidth(ci);
34964                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34965                 if(currentWidth != cw){
34966                     cm.setColumnWidth(ci, cw, true);
34967                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34968                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34969                     gv.updateSplitters();
34970                     gv.layout(false, true);
34971                 }
34972             }
34973
34974             if(initialRender){
34975                 lw.show();
34976                 mw.show();
34977             }
34978             //c.endMeasure();
34979         }, 10);
34980     },
34981
34982     onWindowResize : function(){
34983         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34984             return;
34985         }
34986         this.layout();
34987     },
34988
34989     appendFooter : function(parentEl){
34990         return null;
34991     },
34992
34993     sortAscText : "Sort Ascending",
34994     sortDescText : "Sort Descending",
34995     lockText : "Lock Column",
34996     unlockText : "Unlock Column",
34997     columnsText : "Columns",
34998  
34999     columnsWiderText : "Wider",
35000     columnsNarrowText : "Thinner"
35001 });
35002
35003
35004 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
35005     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
35006     this.proxy.el.addClass('x-grid3-col-dd');
35007 };
35008
35009 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
35010     handleMouseDown : function(e){
35011
35012     },
35013
35014     callHandleMouseDown : function(e){
35015         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
35016     }
35017 });
35018 /*
35019  * Based on:
35020  * Ext JS Library 1.1.1
35021  * Copyright(c) 2006-2007, Ext JS, LLC.
35022  *
35023  * Originally Released Under LGPL - original licence link has changed is not relivant.
35024  *
35025  * Fork - LGPL
35026  * <script type="text/javascript">
35027  */
35028  /**
35029  * @extends Roo.dd.DDProxy
35030  * @class Roo.grid.SplitDragZone
35031  * Support for Column Header resizing
35032  * @constructor
35033  * @param {Object} config
35034  */
35035 // private
35036 // This is a support class used internally by the Grid components
35037 Roo.grid.SplitDragZone = function(grid, hd, hd2){
35038     this.grid = grid;
35039     this.view = grid.getView();
35040     this.proxy = this.view.resizeProxy;
35041     Roo.grid.SplitDragZone.superclass.constructor.call(
35042         this,
35043         hd, // ID
35044         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
35045         {  // CONFIG
35046             dragElId : Roo.id(this.proxy.dom),
35047             resizeFrame:false
35048         }
35049     );
35050     
35051     this.setHandleElId(Roo.id(hd));
35052     if (hd2 !== false) {
35053         this.setOuterHandleElId(Roo.id(hd2));
35054     }
35055     
35056     this.scroll = false;
35057 };
35058 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
35059     fly: Roo.Element.fly,
35060
35061     b4StartDrag : function(x, y){
35062         this.view.headersDisabled = true;
35063         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
35064                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
35065         );
35066         this.proxy.setHeight(h);
35067         
35068         // for old system colWidth really stored the actual width?
35069         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
35070         // which in reality did not work.. - it worked only for fixed sizes
35071         // for resizable we need to use actual sizes.
35072         var w = this.cm.getColumnWidth(this.cellIndex);
35073         if (!this.view.mainWrap) {
35074             // bootstrap.
35075             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
35076         }
35077         
35078         
35079         
35080         // this was w-this.grid.minColumnWidth;
35081         // doesnt really make sense? - w = thie curren width or the rendered one?
35082         var minw = Math.max(w-this.grid.minColumnWidth, 0);
35083         this.resetConstraints();
35084         this.setXConstraint(minw, 1000);
35085         this.setYConstraint(0, 0);
35086         this.minX = x - minw;
35087         this.maxX = x + 1000;
35088         this.startPos = x;
35089         if (!this.view.mainWrap) { // this is Bootstrap code..
35090             this.getDragEl().style.display='block';
35091         }
35092         
35093         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
35094     },
35095
35096
35097     handleMouseDown : function(e){
35098         ev = Roo.EventObject.setEvent(e);
35099         var t = this.fly(ev.getTarget());
35100         if(t.hasClass("x-grid-split")){
35101             this.cellIndex = this.view.getCellIndex(t.dom);
35102             this.split = t.dom;
35103             this.cm = this.grid.colModel;
35104             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
35105                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
35106             }
35107         }
35108     },
35109
35110     endDrag : function(e){
35111         this.view.headersDisabled = false;
35112         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
35113         var diff = endX - this.startPos;
35114         // 
35115         var w = this.cm.getColumnWidth(this.cellIndex);
35116         if (!this.view.mainWrap) {
35117             w = 0;
35118         }
35119         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
35120     },
35121
35122     autoOffset : function(){
35123         this.setDelta(0,0);
35124     }
35125 });/*
35126  * Based on:
35127  * Ext JS Library 1.1.1
35128  * Copyright(c) 2006-2007, Ext JS, LLC.
35129  *
35130  * Originally Released Under LGPL - original licence link has changed is not relivant.
35131  *
35132  * Fork - LGPL
35133  * <script type="text/javascript">
35134  */
35135  
35136 // private
35137 // This is a support class used internally by the Grid components
35138 Roo.grid.GridDragZone = function(grid, config){
35139     this.view = grid.getView();
35140     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
35141     if(this.view.lockedBody){
35142         this.setHandleElId(Roo.id(this.view.mainBody.dom));
35143         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
35144     }
35145     this.scroll = false;
35146     this.grid = grid;
35147     this.ddel = document.createElement('div');
35148     this.ddel.className = 'x-grid-dd-wrap';
35149 };
35150
35151 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
35152     ddGroup : "GridDD",
35153
35154     getDragData : function(e){
35155         var t = Roo.lib.Event.getTarget(e);
35156         var rowIndex = this.view.findRowIndex(t);
35157         var sm = this.grid.selModel;
35158             
35159         //Roo.log(rowIndex);
35160         
35161         if (sm.getSelectedCell) {
35162             // cell selection..
35163             if (!sm.getSelectedCell()) {
35164                 return false;
35165             }
35166             if (rowIndex != sm.getSelectedCell()[0]) {
35167                 return false;
35168             }
35169         
35170         }
35171         if (sm.getSelections && sm.getSelections().length < 1) {
35172             return false;
35173         }
35174         
35175         
35176         // before it used to all dragging of unseleted... - now we dont do that.
35177         if(rowIndex !== false){
35178             
35179             // if editorgrid.. 
35180             
35181             
35182             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
35183                
35184             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
35185               //  
35186             //}
35187             if (e.hasModifier()){
35188                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
35189             }
35190             
35191             Roo.log("getDragData");
35192             
35193             return {
35194                 grid: this.grid,
35195                 ddel: this.ddel,
35196                 rowIndex: rowIndex,
35197                 selections: sm.getSelections ? sm.getSelections() : (
35198                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
35199             };
35200         }
35201         return false;
35202     },
35203     
35204     
35205     onInitDrag : function(e){
35206         var data = this.dragData;
35207         this.ddel.innerHTML = this.grid.getDragDropText();
35208         this.proxy.update(this.ddel);
35209         // fire start drag?
35210     },
35211
35212     afterRepair : function(){
35213         this.dragging = false;
35214     },
35215
35216     getRepairXY : function(e, data){
35217         return false;
35218     },
35219
35220     onEndDrag : function(data, e){
35221         // fire end drag?
35222     },
35223
35224     onValidDrop : function(dd, e, id){
35225         // fire drag drop?
35226         this.hideProxy();
35227     },
35228
35229     beforeInvalidDrop : function(e, id){
35230
35231     }
35232 });/*
35233  * Based on:
35234  * Ext JS Library 1.1.1
35235  * Copyright(c) 2006-2007, Ext JS, LLC.
35236  *
35237  * Originally Released Under LGPL - original licence link has changed is not relivant.
35238  *
35239  * Fork - LGPL
35240  * <script type="text/javascript">
35241  */
35242  
35243
35244 /**
35245  * @class Roo.grid.ColumnModel
35246  * @extends Roo.util.Observable
35247  * This is the default implementation of a ColumnModel used by the Grid. It defines
35248  * the columns in the grid.
35249  * <br>Usage:<br>
35250  <pre><code>
35251  var colModel = new Roo.grid.ColumnModel([
35252         {header: "Ticker", width: 60, sortable: true, locked: true},
35253         {header: "Company Name", width: 150, sortable: true},
35254         {header: "Market Cap.", width: 100, sortable: true},
35255         {header: "$ Sales", width: 100, sortable: true, renderer: money},
35256         {header: "Employees", width: 100, sortable: true, resizable: false}
35257  ]);
35258  </code></pre>
35259  * <p>
35260  
35261  * The config options listed for this class are options which may appear in each
35262  * individual column definition.
35263  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35264  * @constructor
35265  * @param {Object} config An Array of column config objects. See this class's
35266  * config objects for details.
35267 */
35268 Roo.grid.ColumnModel = function(config){
35269         /**
35270      * The config passed into the constructor
35271      */
35272     this.config = []; //config;
35273     this.lookup = {};
35274
35275     // if no id, create one
35276     // if the column does not have a dataIndex mapping,
35277     // map it to the order it is in the config
35278     for(var i = 0, len = config.length; i < len; i++){
35279         this.addColumn(config[i]);
35280         
35281     }
35282
35283     /**
35284      * The width of columns which have no width specified (defaults to 100)
35285      * @type Number
35286      */
35287     this.defaultWidth = 100;
35288
35289     /**
35290      * Default sortable of columns which have no sortable specified (defaults to false)
35291      * @type Boolean
35292      */
35293     this.defaultSortable = false;
35294
35295     this.addEvents({
35296         /**
35297              * @event widthchange
35298              * Fires when the width of a column changes.
35299              * @param {ColumnModel} this
35300              * @param {Number} columnIndex The column index
35301              * @param {Number} newWidth The new width
35302              */
35303             "widthchange": true,
35304         /**
35305              * @event headerchange
35306              * Fires when the text of a header changes.
35307              * @param {ColumnModel} this
35308              * @param {Number} columnIndex The column index
35309              * @param {Number} newText The new header text
35310              */
35311             "headerchange": true,
35312         /**
35313              * @event hiddenchange
35314              * Fires when a column is hidden or "unhidden".
35315              * @param {ColumnModel} this
35316              * @param {Number} columnIndex The column index
35317              * @param {Boolean} hidden true if hidden, false otherwise
35318              */
35319             "hiddenchange": true,
35320             /**
35321          * @event columnmoved
35322          * Fires when a column is moved.
35323          * @param {ColumnModel} this
35324          * @param {Number} oldIndex
35325          * @param {Number} newIndex
35326          */
35327         "columnmoved" : true,
35328         /**
35329          * @event columlockchange
35330          * Fires when a column's locked state is changed
35331          * @param {ColumnModel} this
35332          * @param {Number} colIndex
35333          * @param {Boolean} locked true if locked
35334          */
35335         "columnlockchange" : true
35336     });
35337     Roo.grid.ColumnModel.superclass.constructor.call(this);
35338 };
35339 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35340     /**
35341      * @cfg {String} header The header text to display in the Grid view.
35342      */
35343         /**
35344      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
35345      */
35346         /**
35347      * @cfg {String} smHeader Header at Bootsrap Small width
35348      */
35349         /**
35350      * @cfg {String} mdHeader Header at Bootsrap Medium width
35351      */
35352         /**
35353      * @cfg {String} lgHeader Header at Bootsrap Large width
35354      */
35355         /**
35356      * @cfg {String} xlHeader Header at Bootsrap extra Large width
35357      */
35358     /**
35359      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35360      * {@link Roo.data.Record} definition from which to draw the column's value. If not
35361      * specified, the column's index is used as an index into the Record's data Array.
35362      */
35363     /**
35364      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35365      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35366      */
35367     /**
35368      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35369      * Defaults to the value of the {@link #defaultSortable} property.
35370      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35371      */
35372     /**
35373      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
35374      */
35375     /**
35376      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
35377      */
35378     /**
35379      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35380      */
35381     /**
35382      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35383      */
35384     /**
35385      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35386      * given the cell's data value. See {@link #setRenderer}. If not specified, the
35387      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
35388      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
35389      */
35390        /**
35391      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
35392      */
35393     /**
35394      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
35395      */
35396     /**
35397      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
35398      */
35399     /**
35400      * @cfg {String} cursor (Optional)
35401      */
35402     /**
35403      * @cfg {String} tooltip (Optional)
35404      */
35405     /**
35406      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
35407      */
35408     /**
35409      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
35410      */
35411     /**
35412      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
35413      */
35414     /**
35415      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
35416      */
35417         /**
35418      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
35419      */
35420     /**
35421      * Returns the id of the column at the specified index.
35422      * @param {Number} index The column index
35423      * @return {String} the id
35424      */
35425     getColumnId : function(index){
35426         return this.config[index].id;
35427     },
35428
35429     /**
35430      * Returns the column for a specified id.
35431      * @param {String} id The column id
35432      * @return {Object} the column
35433      */
35434     getColumnById : function(id){
35435         return this.lookup[id];
35436     },
35437
35438     
35439     /**
35440      * Returns the column Object for a specified dataIndex.
35441      * @param {String} dataIndex The column dataIndex
35442      * @return {Object|Boolean} the column or false if not found
35443      */
35444     getColumnByDataIndex: function(dataIndex){
35445         var index = this.findColumnIndex(dataIndex);
35446         return index > -1 ? this.config[index] : false;
35447     },
35448     
35449     /**
35450      * Returns the index for a specified column id.
35451      * @param {String} id The column id
35452      * @return {Number} the index, or -1 if not found
35453      */
35454     getIndexById : function(id){
35455         for(var i = 0, len = this.config.length; i < len; i++){
35456             if(this.config[i].id == id){
35457                 return i;
35458             }
35459         }
35460         return -1;
35461     },
35462     
35463     /**
35464      * Returns the index for a specified column dataIndex.
35465      * @param {String} dataIndex The column dataIndex
35466      * @return {Number} the index, or -1 if not found
35467      */
35468     
35469     findColumnIndex : function(dataIndex){
35470         for(var i = 0, len = this.config.length; i < len; i++){
35471             if(this.config[i].dataIndex == dataIndex){
35472                 return i;
35473             }
35474         }
35475         return -1;
35476     },
35477     
35478     
35479     moveColumn : function(oldIndex, newIndex){
35480         var c = this.config[oldIndex];
35481         this.config.splice(oldIndex, 1);
35482         this.config.splice(newIndex, 0, c);
35483         this.dataMap = null;
35484         this.fireEvent("columnmoved", this, oldIndex, newIndex);
35485     },
35486
35487     isLocked : function(colIndex){
35488         return this.config[colIndex].locked === true;
35489     },
35490
35491     setLocked : function(colIndex, value, suppressEvent){
35492         if(this.isLocked(colIndex) == value){
35493             return;
35494         }
35495         this.config[colIndex].locked = value;
35496         if(!suppressEvent){
35497             this.fireEvent("columnlockchange", this, colIndex, value);
35498         }
35499     },
35500
35501     getTotalLockedWidth : function(){
35502         var totalWidth = 0;
35503         for(var i = 0; i < this.config.length; i++){
35504             if(this.isLocked(i) && !this.isHidden(i)){
35505                 this.totalWidth += this.getColumnWidth(i);
35506             }
35507         }
35508         return totalWidth;
35509     },
35510
35511     getLockedCount : function(){
35512         for(var i = 0, len = this.config.length; i < len; i++){
35513             if(!this.isLocked(i)){
35514                 return i;
35515             }
35516         }
35517         
35518         return this.config.length;
35519     },
35520
35521     /**
35522      * Returns the number of columns.
35523      * @return {Number}
35524      */
35525     getColumnCount : function(visibleOnly){
35526         if(visibleOnly === true){
35527             var c = 0;
35528             for(var i = 0, len = this.config.length; i < len; i++){
35529                 if(!this.isHidden(i)){
35530                     c++;
35531                 }
35532             }
35533             return c;
35534         }
35535         return this.config.length;
35536     },
35537
35538     /**
35539      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35540      * @param {Function} fn
35541      * @param {Object} scope (optional)
35542      * @return {Array} result
35543      */
35544     getColumnsBy : function(fn, scope){
35545         var r = [];
35546         for(var i = 0, len = this.config.length; i < len; i++){
35547             var c = this.config[i];
35548             if(fn.call(scope||this, c, i) === true){
35549                 r[r.length] = c;
35550             }
35551         }
35552         return r;
35553     },
35554
35555     /**
35556      * Returns true if the specified column is sortable.
35557      * @param {Number} col The column index
35558      * @return {Boolean}
35559      */
35560     isSortable : function(col){
35561         if(typeof this.config[col].sortable == "undefined"){
35562             return this.defaultSortable;
35563         }
35564         return this.config[col].sortable;
35565     },
35566
35567     /**
35568      * Returns the rendering (formatting) function defined for the column.
35569      * @param {Number} col The column index.
35570      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35571      */
35572     getRenderer : function(col){
35573         if(!this.config[col].renderer){
35574             return Roo.grid.ColumnModel.defaultRenderer;
35575         }
35576         return this.config[col].renderer;
35577     },
35578
35579     /**
35580      * Sets the rendering (formatting) function for a column.
35581      * @param {Number} col The column index
35582      * @param {Function} fn The function to use to process the cell's raw data
35583      * to return HTML markup for the grid view. The render function is called with
35584      * the following parameters:<ul>
35585      * <li>Data value.</li>
35586      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35587      * <li>css A CSS style string to apply to the table cell.</li>
35588      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35589      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35590      * <li>Row index</li>
35591      * <li>Column index</li>
35592      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35593      */
35594     setRenderer : function(col, fn){
35595         this.config[col].renderer = fn;
35596     },
35597
35598     /**
35599      * Returns the width for the specified column.
35600      * @param {Number} col The column index
35601      * @param (optional) {String} gridSize bootstrap width size.
35602      * @return {Number}
35603      */
35604     getColumnWidth : function(col, gridSize)
35605         {
35606                 var cfg = this.config[col];
35607                 
35608                 if (typeof(gridSize) == 'undefined') {
35609                         return cfg.width * 1 || this.defaultWidth;
35610                 }
35611                 if (gridSize === false) { // if we set it..
35612                         return cfg.width || false;
35613                 }
35614                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
35615                 
35616                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
35617                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
35618                                 continue;
35619                         }
35620                         return cfg[ sizes[i] ];
35621                 }
35622                 return 1;
35623                 
35624     },
35625
35626     /**
35627      * Sets the width for a column.
35628      * @param {Number} col The column index
35629      * @param {Number} width The new width
35630      */
35631     setColumnWidth : function(col, width, suppressEvent){
35632         this.config[col].width = width;
35633         this.totalWidth = null;
35634         if(!suppressEvent){
35635              this.fireEvent("widthchange", this, col, width);
35636         }
35637     },
35638
35639     /**
35640      * Returns the total width of all columns.
35641      * @param {Boolean} includeHidden True to include hidden column widths
35642      * @return {Number}
35643      */
35644     getTotalWidth : function(includeHidden){
35645         if(!this.totalWidth){
35646             this.totalWidth = 0;
35647             for(var i = 0, len = this.config.length; i < len; i++){
35648                 if(includeHidden || !this.isHidden(i)){
35649                     this.totalWidth += this.getColumnWidth(i);
35650                 }
35651             }
35652         }
35653         return this.totalWidth;
35654     },
35655
35656     /**
35657      * Returns the header for the specified column.
35658      * @param {Number} col The column index
35659      * @return {String}
35660      */
35661     getColumnHeader : function(col){
35662         return this.config[col].header;
35663     },
35664
35665     /**
35666      * Sets the header for a column.
35667      * @param {Number} col The column index
35668      * @param {String} header The new header
35669      */
35670     setColumnHeader : function(col, header){
35671         this.config[col].header = header;
35672         this.fireEvent("headerchange", this, col, header);
35673     },
35674
35675     /**
35676      * Returns the tooltip for the specified column.
35677      * @param {Number} col The column index
35678      * @return {String}
35679      */
35680     getColumnTooltip : function(col){
35681             return this.config[col].tooltip;
35682     },
35683     /**
35684      * Sets the tooltip for a column.
35685      * @param {Number} col The column index
35686      * @param {String} tooltip The new tooltip
35687      */
35688     setColumnTooltip : function(col, tooltip){
35689             this.config[col].tooltip = tooltip;
35690     },
35691
35692     /**
35693      * Returns the dataIndex for the specified column.
35694      * @param {Number} col The column index
35695      * @return {Number}
35696      */
35697     getDataIndex : function(col){
35698         return this.config[col].dataIndex;
35699     },
35700
35701     /**
35702      * Sets the dataIndex for a column.
35703      * @param {Number} col The column index
35704      * @param {Number} dataIndex The new dataIndex
35705      */
35706     setDataIndex : function(col, dataIndex){
35707         this.config[col].dataIndex = dataIndex;
35708     },
35709
35710     
35711     
35712     /**
35713      * Returns true if the cell is editable.
35714      * @param {Number} colIndex The column index
35715      * @param {Number} rowIndex The row index - this is nto actually used..?
35716      * @return {Boolean}
35717      */
35718     isCellEditable : function(colIndex, rowIndex){
35719         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35720     },
35721
35722     /**
35723      * Returns the editor defined for the cell/column.
35724      * return false or null to disable editing.
35725      * @param {Number} colIndex The column index
35726      * @param {Number} rowIndex The row index
35727      * @return {Object}
35728      */
35729     getCellEditor : function(colIndex, rowIndex){
35730         return this.config[colIndex].editor;
35731     },
35732
35733     /**
35734      * Sets if a column is editable.
35735      * @param {Number} col The column index
35736      * @param {Boolean} editable True if the column is editable
35737      */
35738     setEditable : function(col, editable){
35739         this.config[col].editable = editable;
35740     },
35741
35742
35743     /**
35744      * Returns true if the column is hidden.
35745      * @param {Number} colIndex The column index
35746      * @return {Boolean}
35747      */
35748     isHidden : function(colIndex){
35749         return this.config[colIndex].hidden;
35750     },
35751
35752
35753     /**
35754      * Returns true if the column width cannot be changed
35755      */
35756     isFixed : function(colIndex){
35757         return this.config[colIndex].fixed;
35758     },
35759
35760     /**
35761      * Returns true if the column can be resized
35762      * @return {Boolean}
35763      */
35764     isResizable : function(colIndex){
35765         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35766     },
35767     /**
35768      * Sets if a column is hidden.
35769      * @param {Number} colIndex The column index
35770      * @param {Boolean} hidden True if the column is hidden
35771      */
35772     setHidden : function(colIndex, hidden){
35773         this.config[colIndex].hidden = hidden;
35774         this.totalWidth = null;
35775         this.fireEvent("hiddenchange", this, colIndex, hidden);
35776     },
35777
35778     /**
35779      * Sets the editor for a column.
35780      * @param {Number} col The column index
35781      * @param {Object} editor The editor object
35782      */
35783     setEditor : function(col, editor){
35784         this.config[col].editor = editor;
35785     },
35786     /**
35787      * Add a column (experimental...) - defaults to adding to the end..
35788      * @param {Object} config 
35789     */
35790     addColumn : function(c)
35791     {
35792     
35793         var i = this.config.length;
35794         this.config[i] = c;
35795         
35796         if(typeof c.dataIndex == "undefined"){
35797             c.dataIndex = i;
35798         }
35799         if(typeof c.renderer == "string"){
35800             c.renderer = Roo.util.Format[c.renderer];
35801         }
35802         if(typeof c.id == "undefined"){
35803             c.id = Roo.id();
35804         }
35805         if(c.editor && c.editor.xtype){
35806             c.editor  = Roo.factory(c.editor, Roo.grid);
35807         }
35808         if(c.editor && c.editor.isFormField){
35809             c.editor = new Roo.grid.GridEditor(c.editor);
35810         }
35811         this.lookup[c.id] = c;
35812     }
35813     
35814 });
35815
35816 Roo.grid.ColumnModel.defaultRenderer = function(value)
35817 {
35818     if(typeof value == "object") {
35819         return value;
35820     }
35821         if(typeof value == "string" && value.length < 1){
35822             return "&#160;";
35823         }
35824     
35825         return String.format("{0}", value);
35826 };
35827
35828 // Alias for backwards compatibility
35829 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35830 /*
35831  * Based on:
35832  * Ext JS Library 1.1.1
35833  * Copyright(c) 2006-2007, Ext JS, LLC.
35834  *
35835  * Originally Released Under LGPL - original licence link has changed is not relivant.
35836  *
35837  * Fork - LGPL
35838  * <script type="text/javascript">
35839  */
35840
35841 /**
35842  * @class Roo.grid.AbstractSelectionModel
35843  * @extends Roo.util.Observable
35844  * @abstract
35845  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35846  * implemented by descendant classes.  This class should not be directly instantiated.
35847  * @constructor
35848  */
35849 Roo.grid.AbstractSelectionModel = function(){
35850     this.locked = false;
35851     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35852 };
35853
35854 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35855     /** @ignore Called by the grid automatically. Do not call directly. */
35856     init : function(grid){
35857         this.grid = grid;
35858         this.initEvents();
35859     },
35860
35861     /**
35862      * Locks the selections.
35863      */
35864     lock : function(){
35865         this.locked = true;
35866     },
35867
35868     /**
35869      * Unlocks the selections.
35870      */
35871     unlock : function(){
35872         this.locked = false;
35873     },
35874
35875     /**
35876      * Returns true if the selections are locked.
35877      * @return {Boolean}
35878      */
35879     isLocked : function(){
35880         return this.locked;
35881     }
35882 });/*
35883  * Based on:
35884  * Ext JS Library 1.1.1
35885  * Copyright(c) 2006-2007, Ext JS, LLC.
35886  *
35887  * Originally Released Under LGPL - original licence link has changed is not relivant.
35888  *
35889  * Fork - LGPL
35890  * <script type="text/javascript">
35891  */
35892 /**
35893  * @extends Roo.grid.AbstractSelectionModel
35894  * @class Roo.grid.RowSelectionModel
35895  * The default SelectionModel used by {@link Roo.grid.Grid}.
35896  * It supports multiple selections and keyboard selection/navigation. 
35897  * @constructor
35898  * @param {Object} config
35899  */
35900 Roo.grid.RowSelectionModel = function(config){
35901     Roo.apply(this, config);
35902     this.selections = new Roo.util.MixedCollection(false, function(o){
35903         return o.id;
35904     });
35905
35906     this.last = false;
35907     this.lastActive = false;
35908
35909     this.addEvents({
35910         /**
35911         * @event selectionchange
35912         * Fires when the selection changes
35913         * @param {SelectionModel} this
35914         */
35915        "selectionchange" : true,
35916        /**
35917         * @event afterselectionchange
35918         * Fires after the selection changes (eg. by key press or clicking)
35919         * @param {SelectionModel} this
35920         */
35921        "afterselectionchange" : true,
35922        /**
35923         * @event beforerowselect
35924         * Fires when a row is selected being selected, return false to cancel.
35925         * @param {SelectionModel} this
35926         * @param {Number} rowIndex The selected index
35927         * @param {Boolean} keepExisting False if other selections will be cleared
35928         */
35929        "beforerowselect" : true,
35930        /**
35931         * @event rowselect
35932         * Fires when a row is selected.
35933         * @param {SelectionModel} this
35934         * @param {Number} rowIndex The selected index
35935         * @param {Roo.data.Record} r The record
35936         */
35937        "rowselect" : true,
35938        /**
35939         * @event rowdeselect
35940         * Fires when a row is deselected.
35941         * @param {SelectionModel} this
35942         * @param {Number} rowIndex The selected index
35943         */
35944         "rowdeselect" : true
35945     });
35946     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35947     this.locked = false;
35948 };
35949
35950 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35951     /**
35952      * @cfg {Boolean} singleSelect
35953      * True to allow selection of only one row at a time (defaults to false)
35954      */
35955     singleSelect : false,
35956
35957     // private
35958     initEvents : function(){
35959
35960         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35961             this.grid.on("mousedown", this.handleMouseDown, this);
35962         }else{ // allow click to work like normal
35963             this.grid.on("rowclick", this.handleDragableRowClick, this);
35964         }
35965         // bootstrap does not have a view..
35966         var view = this.grid.view ? this.grid.view : this.grid;
35967         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35968             "up" : function(e){
35969                 if(!e.shiftKey){
35970                     this.selectPrevious(e.shiftKey);
35971                 }else if(this.last !== false && this.lastActive !== false){
35972                     var last = this.last;
35973                     this.selectRange(this.last,  this.lastActive-1);
35974                     view.focusRow(this.lastActive);
35975                     if(last !== false){
35976                         this.last = last;
35977                     }
35978                 }else{
35979                     this.selectFirstRow();
35980                 }
35981                 this.fireEvent("afterselectionchange", this);
35982             },
35983             "down" : function(e){
35984                 if(!e.shiftKey){
35985                     this.selectNext(e.shiftKey);
35986                 }else if(this.last !== false && this.lastActive !== false){
35987                     var last = this.last;
35988                     this.selectRange(this.last,  this.lastActive+1);
35989                     view.focusRow(this.lastActive);
35990                     if(last !== false){
35991                         this.last = last;
35992                     }
35993                 }else{
35994                     this.selectFirstRow();
35995                 }
35996                 this.fireEvent("afterselectionchange", this);
35997             },
35998             scope: this
35999         });
36000
36001          
36002         view.on("refresh", this.onRefresh, this);
36003         view.on("rowupdated", this.onRowUpdated, this);
36004         view.on("rowremoved", this.onRemove, this);
36005     },
36006
36007     // private
36008     onRefresh : function(){
36009         var ds = this.grid.ds, i, v = this.grid.view;
36010         var s = this.selections;
36011         s.each(function(r){
36012             if((i = ds.indexOfId(r.id)) != -1){
36013                 v.onRowSelect(i);
36014                 s.add(ds.getAt(i)); // updating the selection relate data
36015             }else{
36016                 s.remove(r);
36017             }
36018         });
36019     },
36020
36021     // private
36022     onRemove : function(v, index, r){
36023         this.selections.remove(r);
36024     },
36025
36026     // private
36027     onRowUpdated : function(v, index, r){
36028         if(this.isSelected(r)){
36029             v.onRowSelect(index);
36030         }
36031     },
36032
36033     /**
36034      * Select records.
36035      * @param {Array} records The records to select
36036      * @param {Boolean} keepExisting (optional) True to keep existing selections
36037      */
36038     selectRecords : function(records, keepExisting){
36039         if(!keepExisting){
36040             this.clearSelections();
36041         }
36042         var ds = this.grid.ds;
36043         for(var i = 0, len = records.length; i < len; i++){
36044             this.selectRow(ds.indexOf(records[i]), true);
36045         }
36046     },
36047
36048     /**
36049      * Gets the number of selected rows.
36050      * @return {Number}
36051      */
36052     getCount : function(){
36053         return this.selections.length;
36054     },
36055
36056     /**
36057      * Selects the first row in the grid.
36058      */
36059     selectFirstRow : function(){
36060         this.selectRow(0);
36061     },
36062
36063     /**
36064      * Select the last row.
36065      * @param {Boolean} keepExisting (optional) True to keep existing selections
36066      */
36067     selectLastRow : function(keepExisting){
36068         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
36069     },
36070
36071     /**
36072      * Selects the row immediately following the last selected row.
36073      * @param {Boolean} keepExisting (optional) True to keep existing selections
36074      */
36075     selectNext : function(keepExisting){
36076         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
36077             this.selectRow(this.last+1, keepExisting);
36078             var view = this.grid.view ? this.grid.view : this.grid;
36079             view.focusRow(this.last);
36080         }
36081     },
36082
36083     /**
36084      * Selects the row that precedes the last selected row.
36085      * @param {Boolean} keepExisting (optional) True to keep existing selections
36086      */
36087     selectPrevious : function(keepExisting){
36088         if(this.last){
36089             this.selectRow(this.last-1, keepExisting);
36090             var view = this.grid.view ? this.grid.view : this.grid;
36091             view.focusRow(this.last);
36092         }
36093     },
36094
36095     /**
36096      * Returns the selected records
36097      * @return {Array} Array of selected records
36098      */
36099     getSelections : function(){
36100         return [].concat(this.selections.items);
36101     },
36102
36103     /**
36104      * Returns the first selected record.
36105      * @return {Record}
36106      */
36107     getSelected : function(){
36108         return this.selections.itemAt(0);
36109     },
36110
36111
36112     /**
36113      * Clears all selections.
36114      */
36115     clearSelections : function(fast){
36116         if(this.locked) {
36117             return;
36118         }
36119         if(fast !== true){
36120             var ds = this.grid.ds;
36121             var s = this.selections;
36122             s.each(function(r){
36123                 this.deselectRow(ds.indexOfId(r.id));
36124             }, this);
36125             s.clear();
36126         }else{
36127             this.selections.clear();
36128         }
36129         this.last = false;
36130     },
36131
36132
36133     /**
36134      * Selects all rows.
36135      */
36136     selectAll : function(){
36137         if(this.locked) {
36138             return;
36139         }
36140         this.selections.clear();
36141         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
36142             this.selectRow(i, true);
36143         }
36144     },
36145
36146     /**
36147      * Returns True if there is a selection.
36148      * @return {Boolean}
36149      */
36150     hasSelection : function(){
36151         return this.selections.length > 0;
36152     },
36153
36154     /**
36155      * Returns True if the specified row is selected.
36156      * @param {Number/Record} record The record or index of the record to check
36157      * @return {Boolean}
36158      */
36159     isSelected : function(index){
36160         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
36161         return (r && this.selections.key(r.id) ? true : false);
36162     },
36163
36164     /**
36165      * Returns True if the specified record id is selected.
36166      * @param {String} id The id of record to check
36167      * @return {Boolean}
36168      */
36169     isIdSelected : function(id){
36170         return (this.selections.key(id) ? true : false);
36171     },
36172
36173     // private
36174     handleMouseDown : function(e, t)
36175     {
36176         var view = this.grid.view ? this.grid.view : this.grid;
36177         var rowIndex;
36178         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
36179             return;
36180         };
36181         if(e.shiftKey && this.last !== false){
36182             var last = this.last;
36183             this.selectRange(last, rowIndex, e.ctrlKey);
36184             this.last = last; // reset the last
36185             view.focusRow(rowIndex);
36186         }else{
36187             var isSelected = this.isSelected(rowIndex);
36188             if(e.button !== 0 && isSelected){
36189                 view.focusRow(rowIndex);
36190             }else if(e.ctrlKey && isSelected){
36191                 this.deselectRow(rowIndex);
36192             }else if(!isSelected){
36193                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
36194                 view.focusRow(rowIndex);
36195             }
36196         }
36197         this.fireEvent("afterselectionchange", this);
36198     },
36199     // private
36200     handleDragableRowClick :  function(grid, rowIndex, e) 
36201     {
36202         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
36203             this.selectRow(rowIndex, false);
36204             var view = this.grid.view ? this.grid.view : this.grid;
36205             view.focusRow(rowIndex);
36206              this.fireEvent("afterselectionchange", this);
36207         }
36208     },
36209     
36210     /**
36211      * Selects multiple rows.
36212      * @param {Array} rows Array of the indexes of the row to select
36213      * @param {Boolean} keepExisting (optional) True to keep existing selections
36214      */
36215     selectRows : function(rows, keepExisting){
36216         if(!keepExisting){
36217             this.clearSelections();
36218         }
36219         for(var i = 0, len = rows.length; i < len; i++){
36220             this.selectRow(rows[i], true);
36221         }
36222     },
36223
36224     /**
36225      * Selects a range of rows. All rows in between startRow and endRow are also selected.
36226      * @param {Number} startRow The index of the first row in the range
36227      * @param {Number} endRow The index of the last row in the range
36228      * @param {Boolean} keepExisting (optional) True to retain existing selections
36229      */
36230     selectRange : function(startRow, endRow, keepExisting){
36231         if(this.locked) {
36232             return;
36233         }
36234         if(!keepExisting){
36235             this.clearSelections();
36236         }
36237         if(startRow <= endRow){
36238             for(var i = startRow; i <= endRow; i++){
36239                 this.selectRow(i, true);
36240             }
36241         }else{
36242             for(var i = startRow; i >= endRow; i--){
36243                 this.selectRow(i, true);
36244             }
36245         }
36246     },
36247
36248     /**
36249      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
36250      * @param {Number} startRow The index of the first row in the range
36251      * @param {Number} endRow The index of the last row in the range
36252      */
36253     deselectRange : function(startRow, endRow, preventViewNotify){
36254         if(this.locked) {
36255             return;
36256         }
36257         for(var i = startRow; i <= endRow; i++){
36258             this.deselectRow(i, preventViewNotify);
36259         }
36260     },
36261
36262     /**
36263      * Selects a row.
36264      * @param {Number} row The index of the row to select
36265      * @param {Boolean} keepExisting (optional) True to keep existing selections
36266      */
36267     selectRow : function(index, keepExisting, preventViewNotify){
36268         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
36269             return;
36270         }
36271         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36272             if(!keepExisting || this.singleSelect){
36273                 this.clearSelections();
36274             }
36275             var r = this.grid.ds.getAt(index);
36276             this.selections.add(r);
36277             this.last = this.lastActive = index;
36278             if(!preventViewNotify){
36279                 var view = this.grid.view ? this.grid.view : this.grid;
36280                 view.onRowSelect(index);
36281             }
36282             this.fireEvent("rowselect", this, index, r);
36283             this.fireEvent("selectionchange", this);
36284         }
36285     },
36286
36287     /**
36288      * Deselects a row.
36289      * @param {Number} row The index of the row to deselect
36290      */
36291     deselectRow : function(index, preventViewNotify){
36292         if(this.locked) {
36293             return;
36294         }
36295         if(this.last == index){
36296             this.last = false;
36297         }
36298         if(this.lastActive == index){
36299             this.lastActive = false;
36300         }
36301         var r = this.grid.ds.getAt(index);
36302         this.selections.remove(r);
36303         if(!preventViewNotify){
36304             var view = this.grid.view ? this.grid.view : this.grid;
36305             view.onRowDeselect(index);
36306         }
36307         this.fireEvent("rowdeselect", this, index);
36308         this.fireEvent("selectionchange", this);
36309     },
36310
36311     // private
36312     restoreLast : function(){
36313         if(this._last){
36314             this.last = this._last;
36315         }
36316     },
36317
36318     // private
36319     acceptsNav : function(row, col, cm){
36320         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36321     },
36322
36323     // private
36324     onEditorKey : function(field, e){
36325         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36326         if(k == e.TAB){
36327             e.stopEvent();
36328             ed.completeEdit();
36329             if(e.shiftKey){
36330                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36331             }else{
36332                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36333             }
36334         }else if(k == e.ENTER && !e.ctrlKey){
36335             e.stopEvent();
36336             ed.completeEdit();
36337             if(e.shiftKey){
36338                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36339             }else{
36340                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36341             }
36342         }else if(k == e.ESC){
36343             ed.cancelEdit();
36344         }
36345         if(newCell){
36346             g.startEditing(newCell[0], newCell[1]);
36347         }
36348     }
36349 });/*
36350  * Based on:
36351  * Ext JS Library 1.1.1
36352  * Copyright(c) 2006-2007, Ext JS, LLC.
36353  *
36354  * Originally Released Under LGPL - original licence link has changed is not relivant.
36355  *
36356  * Fork - LGPL
36357  * <script type="text/javascript">
36358  */
36359 /**
36360  * @class Roo.grid.CellSelectionModel
36361  * @extends Roo.grid.AbstractSelectionModel
36362  * This class provides the basic implementation for cell selection in a grid.
36363  * @constructor
36364  * @param {Object} config The object containing the configuration of this model.
36365  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36366  */
36367 Roo.grid.CellSelectionModel = function(config){
36368     Roo.apply(this, config);
36369
36370     this.selection = null;
36371
36372     this.addEvents({
36373         /**
36374              * @event beforerowselect
36375              * Fires before a cell is selected.
36376              * @param {SelectionModel} this
36377              * @param {Number} rowIndex The selected row index
36378              * @param {Number} colIndex The selected cell index
36379              */
36380             "beforecellselect" : true,
36381         /**
36382              * @event cellselect
36383              * Fires when a cell is selected.
36384              * @param {SelectionModel} this
36385              * @param {Number} rowIndex The selected row index
36386              * @param {Number} colIndex The selected cell index
36387              */
36388             "cellselect" : true,
36389         /**
36390              * @event selectionchange
36391              * Fires when the active selection changes.
36392              * @param {SelectionModel} this
36393              * @param {Object} selection null for no selection or an object (o) with two properties
36394                 <ul>
36395                 <li>o.record: the record object for the row the selection is in</li>
36396                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36397                 </ul>
36398              */
36399             "selectionchange" : true,
36400         /**
36401              * @event tabend
36402              * Fires when the tab (or enter) was pressed on the last editable cell
36403              * You can use this to trigger add new row.
36404              * @param {SelectionModel} this
36405              */
36406             "tabend" : true,
36407          /**
36408              * @event beforeeditnext
36409              * Fires before the next editable sell is made active
36410              * You can use this to skip to another cell or fire the tabend
36411              *    if you set cell to false
36412              * @param {Object} eventdata object : { cell : [ row, col ] } 
36413              */
36414             "beforeeditnext" : true
36415     });
36416     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36417 };
36418
36419 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
36420     
36421     enter_is_tab: false,
36422
36423     /** @ignore */
36424     initEvents : function(){
36425         this.grid.on("mousedown", this.handleMouseDown, this);
36426         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36427         var view = this.grid.view;
36428         view.on("refresh", this.onViewChange, this);
36429         view.on("rowupdated", this.onRowUpdated, this);
36430         view.on("beforerowremoved", this.clearSelections, this);
36431         view.on("beforerowsinserted", this.clearSelections, this);
36432         if(this.grid.isEditor){
36433             this.grid.on("beforeedit", this.beforeEdit,  this);
36434         }
36435     },
36436
36437         //private
36438     beforeEdit : function(e){
36439         this.select(e.row, e.column, false, true, e.record);
36440     },
36441
36442         //private
36443     onRowUpdated : function(v, index, r){
36444         if(this.selection && this.selection.record == r){
36445             v.onCellSelect(index, this.selection.cell[1]);
36446         }
36447     },
36448
36449         //private
36450     onViewChange : function(){
36451         this.clearSelections(true);
36452     },
36453
36454         /**
36455          * Returns the currently selected cell,.
36456          * @return {Array} The selected cell (row, column) or null if none selected.
36457          */
36458     getSelectedCell : function(){
36459         return this.selection ? this.selection.cell : null;
36460     },
36461
36462     /**
36463      * Clears all selections.
36464      * @param {Boolean} true to prevent the gridview from being notified about the change.
36465      */
36466     clearSelections : function(preventNotify){
36467         var s = this.selection;
36468         if(s){
36469             if(preventNotify !== true){
36470                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36471             }
36472             this.selection = null;
36473             this.fireEvent("selectionchange", this, null);
36474         }
36475     },
36476
36477     /**
36478      * Returns true if there is a selection.
36479      * @return {Boolean}
36480      */
36481     hasSelection : function(){
36482         return this.selection ? true : false;
36483     },
36484
36485     /** @ignore */
36486     handleMouseDown : function(e, t){
36487         var v = this.grid.getView();
36488         if(this.isLocked()){
36489             return;
36490         };
36491         var row = v.findRowIndex(t);
36492         var cell = v.findCellIndex(t);
36493         if(row !== false && cell !== false){
36494             this.select(row, cell);
36495         }
36496     },
36497
36498     /**
36499      * Selects a cell.
36500      * @param {Number} rowIndex
36501      * @param {Number} collIndex
36502      */
36503     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36504         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36505             this.clearSelections();
36506             r = r || this.grid.dataSource.getAt(rowIndex);
36507             this.selection = {
36508                 record : r,
36509                 cell : [rowIndex, colIndex]
36510             };
36511             if(!preventViewNotify){
36512                 var v = this.grid.getView();
36513                 v.onCellSelect(rowIndex, colIndex);
36514                 if(preventFocus !== true){
36515                     v.focusCell(rowIndex, colIndex);
36516                 }
36517             }
36518             this.fireEvent("cellselect", this, rowIndex, colIndex);
36519             this.fireEvent("selectionchange", this, this.selection);
36520         }
36521     },
36522
36523         //private
36524     isSelectable : function(rowIndex, colIndex, cm){
36525         return !cm.isHidden(colIndex);
36526     },
36527
36528     /** @ignore */
36529     handleKeyDown : function(e){
36530         //Roo.log('Cell Sel Model handleKeyDown');
36531         if(!e.isNavKeyPress()){
36532             return;
36533         }
36534         var g = this.grid, s = this.selection;
36535         if(!s){
36536             e.stopEvent();
36537             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
36538             if(cell){
36539                 this.select(cell[0], cell[1]);
36540             }
36541             return;
36542         }
36543         var sm = this;
36544         var walk = function(row, col, step){
36545             return g.walkCells(row, col, step, sm.isSelectable,  sm);
36546         };
36547         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36548         var newCell;
36549
36550       
36551
36552         switch(k){
36553             case e.TAB:
36554                 // handled by onEditorKey
36555                 if (g.isEditor && g.editing) {
36556                     return;
36557                 }
36558                 if(e.shiftKey) {
36559                     newCell = walk(r, c-1, -1);
36560                 } else {
36561                     newCell = walk(r, c+1, 1);
36562                 }
36563                 break;
36564             
36565             case e.DOWN:
36566                newCell = walk(r+1, c, 1);
36567                 break;
36568             
36569             case e.UP:
36570                 newCell = walk(r-1, c, -1);
36571                 break;
36572             
36573             case e.RIGHT:
36574                 newCell = walk(r, c+1, 1);
36575                 break;
36576             
36577             case e.LEFT:
36578                 newCell = walk(r, c-1, -1);
36579                 break;
36580             
36581             case e.ENTER:
36582                 
36583                 if(g.isEditor && !g.editing){
36584                    g.startEditing(r, c);
36585                    e.stopEvent();
36586                    return;
36587                 }
36588                 
36589                 
36590              break;
36591         };
36592         if(newCell){
36593             this.select(newCell[0], newCell[1]);
36594             e.stopEvent();
36595             
36596         }
36597     },
36598
36599     acceptsNav : function(row, col, cm){
36600         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36601     },
36602     /**
36603      * Selects a cell.
36604      * @param {Number} field (not used) - as it's normally used as a listener
36605      * @param {Number} e - event - fake it by using
36606      *
36607      * var e = Roo.EventObjectImpl.prototype;
36608      * e.keyCode = e.TAB
36609      *
36610      * 
36611      */
36612     onEditorKey : function(field, e){
36613         
36614         var k = e.getKey(),
36615             newCell,
36616             g = this.grid,
36617             ed = g.activeEditor,
36618             forward = false;
36619         ///Roo.log('onEditorKey' + k);
36620         
36621         
36622         if (this.enter_is_tab && k == e.ENTER) {
36623             k = e.TAB;
36624         }
36625         
36626         if(k == e.TAB){
36627             if(e.shiftKey){
36628                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36629             }else{
36630                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36631                 forward = true;
36632             }
36633             
36634             e.stopEvent();
36635             
36636         } else if(k == e.ENTER &&  !e.ctrlKey){
36637             ed.completeEdit();
36638             e.stopEvent();
36639             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36640         
36641                 } else if(k == e.ESC){
36642             ed.cancelEdit();
36643         }
36644                 
36645         if (newCell) {
36646             var ecall = { cell : newCell, forward : forward };
36647             this.fireEvent('beforeeditnext', ecall );
36648             newCell = ecall.cell;
36649                         forward = ecall.forward;
36650         }
36651                 
36652         if(newCell){
36653             //Roo.log('next cell after edit');
36654             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36655         } else if (forward) {
36656             // tabbed past last
36657             this.fireEvent.defer(100, this, ['tabend',this]);
36658         }
36659     }
36660 });/*
36661  * Based on:
36662  * Ext JS Library 1.1.1
36663  * Copyright(c) 2006-2007, Ext JS, LLC.
36664  *
36665  * Originally Released Under LGPL - original licence link has changed is not relivant.
36666  *
36667  * Fork - LGPL
36668  * <script type="text/javascript">
36669  */
36670  
36671 /**
36672  * @class Roo.grid.EditorGrid
36673  * @extends Roo.grid.Grid
36674  * Class for creating and editable grid.
36675  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36676  * The container MUST have some type of size defined for the grid to fill. The container will be 
36677  * automatically set to position relative if it isn't already.
36678  * @param {Object} dataSource The data model to bind to
36679  * @param {Object} colModel The column model with info about this grid's columns
36680  */
36681 Roo.grid.EditorGrid = function(container, config){
36682     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36683     this.getGridEl().addClass("xedit-grid");
36684
36685     if(!this.selModel){
36686         this.selModel = new Roo.grid.CellSelectionModel();
36687     }
36688
36689     this.activeEditor = null;
36690
36691         this.addEvents({
36692             /**
36693              * @event beforeedit
36694              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36695              * <ul style="padding:5px;padding-left:16px;">
36696              * <li>grid - This grid</li>
36697              * <li>record - The record being edited</li>
36698              * <li>field - The field name being edited</li>
36699              * <li>value - The value for the field being edited.</li>
36700              * <li>row - The grid row index</li>
36701              * <li>column - The grid column index</li>
36702              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36703              * </ul>
36704              * @param {Object} e An edit event (see above for description)
36705              */
36706             "beforeedit" : true,
36707             /**
36708              * @event afteredit
36709              * Fires after a cell is edited. <br />
36710              * <ul style="padding:5px;padding-left:16px;">
36711              * <li>grid - This grid</li>
36712              * <li>record - The record being edited</li>
36713              * <li>field - The field name being edited</li>
36714              * <li>value - The value being set</li>
36715              * <li>originalValue - The original value for the field, before the edit.</li>
36716              * <li>row - The grid row index</li>
36717              * <li>column - The grid column index</li>
36718              * </ul>
36719              * @param {Object} e An edit event (see above for description)
36720              */
36721             "afteredit" : true,
36722             /**
36723              * @event validateedit
36724              * Fires after a cell is edited, but before the value is set in the record. 
36725          * You can use this to modify the value being set in the field, Return false
36726              * to cancel the change. The edit event object has the following properties <br />
36727              * <ul style="padding:5px;padding-left:16px;">
36728          * <li>editor - This editor</li>
36729              * <li>grid - This grid</li>
36730              * <li>record - The record being edited</li>
36731              * <li>field - The field name being edited</li>
36732              * <li>value - The value being set</li>
36733              * <li>originalValue - The original value for the field, before the edit.</li>
36734              * <li>row - The grid row index</li>
36735              * <li>column - The grid column index</li>
36736              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36737              * </ul>
36738              * @param {Object} e An edit event (see above for description)
36739              */
36740             "validateedit" : true
36741         });
36742     this.on("bodyscroll", this.stopEditing,  this);
36743     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36744 };
36745
36746 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36747     /**
36748      * @cfg {Number} clicksToEdit
36749      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36750      */
36751     clicksToEdit: 2,
36752
36753     // private
36754     isEditor : true,
36755     // private
36756     trackMouseOver: false, // causes very odd FF errors
36757
36758     onCellDblClick : function(g, row, col){
36759         this.startEditing(row, col);
36760     },
36761
36762     onEditComplete : function(ed, value, startValue){
36763         this.editing = false;
36764         this.activeEditor = null;
36765         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36766         var r = ed.record;
36767         var field = this.colModel.getDataIndex(ed.col);
36768         var e = {
36769             grid: this,
36770             record: r,
36771             field: field,
36772             originalValue: startValue,
36773             value: value,
36774             row: ed.row,
36775             column: ed.col,
36776             cancel:false,
36777             editor: ed
36778         };
36779         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36780         cell.show();
36781           
36782         if(String(value) !== String(startValue)){
36783             
36784             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36785                 r.set(field, e.value);
36786                 // if we are dealing with a combo box..
36787                 // then we also set the 'name' colum to be the displayField
36788                 if (ed.field.displayField && ed.field.name) {
36789                     r.set(ed.field.name, ed.field.el.dom.value);
36790                 }
36791                 
36792                 delete e.cancel; //?? why!!!
36793                 this.fireEvent("afteredit", e);
36794             }
36795         } else {
36796             this.fireEvent("afteredit", e); // always fire it!
36797         }
36798         this.view.focusCell(ed.row, ed.col);
36799     },
36800
36801     /**
36802      * Starts editing the specified for the specified row/column
36803      * @param {Number} rowIndex
36804      * @param {Number} colIndex
36805      */
36806     startEditing : function(row, col){
36807         this.stopEditing();
36808         if(this.colModel.isCellEditable(col, row)){
36809             this.view.ensureVisible(row, col, true);
36810           
36811             var r = this.dataSource.getAt(row);
36812             var field = this.colModel.getDataIndex(col);
36813             var cell = Roo.get(this.view.getCell(row,col));
36814             var e = {
36815                 grid: this,
36816                 record: r,
36817                 field: field,
36818                 value: r.data[field],
36819                 row: row,
36820                 column: col,
36821                 cancel:false 
36822             };
36823             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36824                 this.editing = true;
36825                 var ed = this.colModel.getCellEditor(col, row);
36826                 
36827                 if (!ed) {
36828                     return;
36829                 }
36830                 if(!ed.rendered){
36831                     ed.render(ed.parentEl || document.body);
36832                 }
36833                 ed.field.reset();
36834                
36835                 cell.hide();
36836                 
36837                 (function(){ // complex but required for focus issues in safari, ie and opera
36838                     ed.row = row;
36839                     ed.col = col;
36840                     ed.record = r;
36841                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
36842                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
36843                     this.activeEditor = ed;
36844                     var v = r.data[field];
36845                     ed.startEdit(this.view.getCell(row, col), v);
36846                     // combo's with 'displayField and name set
36847                     if (ed.field.displayField && ed.field.name) {
36848                         ed.field.el.dom.value = r.data[ed.field.name];
36849                     }
36850                     
36851                     
36852                 }).defer(50, this);
36853             }
36854         }
36855     },
36856         
36857     /**
36858      * Stops any active editing
36859      */
36860     stopEditing : function(){
36861         if(this.activeEditor){
36862             this.activeEditor.completeEdit();
36863         }
36864         this.activeEditor = null;
36865     },
36866         
36867          /**
36868      * Called to get grid's drag proxy text, by default returns this.ddText.
36869      * @return {String}
36870      */
36871     getDragDropText : function(){
36872         var count = this.selModel.getSelectedCell() ? 1 : 0;
36873         return String.format(this.ddText, count, count == 1 ? '' : 's');
36874     }
36875         
36876 });/*
36877  * Based on:
36878  * Ext JS Library 1.1.1
36879  * Copyright(c) 2006-2007, Ext JS, LLC.
36880  *
36881  * Originally Released Under LGPL - original licence link has changed is not relivant.
36882  *
36883  * Fork - LGPL
36884  * <script type="text/javascript">
36885  */
36886
36887 // private - not really -- you end up using it !
36888 // This is a support class used internally by the Grid components
36889
36890 /**
36891  * @class Roo.grid.GridEditor
36892  * @extends Roo.Editor
36893  * Class for creating and editable grid elements.
36894  * @param {Object} config any settings (must include field)
36895  */
36896 Roo.grid.GridEditor = function(field, config){
36897     if (!config && field.field) {
36898         config = field;
36899         field = Roo.factory(config.field, Roo.form);
36900     }
36901     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36902     field.monitorTab = false;
36903 };
36904
36905 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36906     
36907     /**
36908      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36909      */
36910     
36911     alignment: "tl-tl",
36912     autoSize: "width",
36913     hideEl : false,
36914     cls: "x-small-editor x-grid-editor",
36915     shim:false,
36916     shadow:"frame"
36917 });/*
36918  * Based on:
36919  * Ext JS Library 1.1.1
36920  * Copyright(c) 2006-2007, Ext JS, LLC.
36921  *
36922  * Originally Released Under LGPL - original licence link has changed is not relivant.
36923  *
36924  * Fork - LGPL
36925  * <script type="text/javascript">
36926  */
36927   
36928
36929   
36930 Roo.grid.PropertyRecord = Roo.data.Record.create([
36931     {name:'name',type:'string'},  'value'
36932 ]);
36933
36934
36935 Roo.grid.PropertyStore = function(grid, source){
36936     this.grid = grid;
36937     this.store = new Roo.data.Store({
36938         recordType : Roo.grid.PropertyRecord
36939     });
36940     this.store.on('update', this.onUpdate,  this);
36941     if(source){
36942         this.setSource(source);
36943     }
36944     Roo.grid.PropertyStore.superclass.constructor.call(this);
36945 };
36946
36947
36948
36949 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36950     setSource : function(o){
36951         this.source = o;
36952         this.store.removeAll();
36953         var data = [];
36954         for(var k in o){
36955             if(this.isEditableValue(o[k])){
36956                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36957             }
36958         }
36959         this.store.loadRecords({records: data}, {}, true);
36960     },
36961
36962     onUpdate : function(ds, record, type){
36963         if(type == Roo.data.Record.EDIT){
36964             var v = record.data['value'];
36965             var oldValue = record.modified['value'];
36966             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36967                 this.source[record.id] = v;
36968                 record.commit();
36969                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36970             }else{
36971                 record.reject();
36972             }
36973         }
36974     },
36975
36976     getProperty : function(row){
36977        return this.store.getAt(row);
36978     },
36979
36980     isEditableValue: function(val){
36981         if(val && val instanceof Date){
36982             return true;
36983         }else if(typeof val == 'object' || typeof val == 'function'){
36984             return false;
36985         }
36986         return true;
36987     },
36988
36989     setValue : function(prop, value){
36990         this.source[prop] = value;
36991         this.store.getById(prop).set('value', value);
36992     },
36993
36994     getSource : function(){
36995         return this.source;
36996     }
36997 });
36998
36999 Roo.grid.PropertyColumnModel = function(grid, store){
37000     this.grid = grid;
37001     var g = Roo.grid;
37002     g.PropertyColumnModel.superclass.constructor.call(this, [
37003         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
37004         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
37005     ]);
37006     this.store = store;
37007     this.bselect = Roo.DomHelper.append(document.body, {
37008         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
37009             {tag: 'option', value: 'true', html: 'true'},
37010             {tag: 'option', value: 'false', html: 'false'}
37011         ]
37012     });
37013     Roo.id(this.bselect);
37014     var f = Roo.form;
37015     this.editors = {
37016         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
37017         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
37018         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
37019         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
37020         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
37021     };
37022     this.renderCellDelegate = this.renderCell.createDelegate(this);
37023     this.renderPropDelegate = this.renderProp.createDelegate(this);
37024 };
37025
37026 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
37027     
37028     
37029     nameText : 'Name',
37030     valueText : 'Value',
37031     
37032     dateFormat : 'm/j/Y',
37033     
37034     
37035     renderDate : function(dateVal){
37036         return dateVal.dateFormat(this.dateFormat);
37037     },
37038
37039     renderBool : function(bVal){
37040         return bVal ? 'true' : 'false';
37041     },
37042
37043     isCellEditable : function(colIndex, rowIndex){
37044         return colIndex == 1;
37045     },
37046
37047     getRenderer : function(col){
37048         return col == 1 ?
37049             this.renderCellDelegate : this.renderPropDelegate;
37050     },
37051
37052     renderProp : function(v){
37053         return this.getPropertyName(v);
37054     },
37055
37056     renderCell : function(val){
37057         var rv = val;
37058         if(val instanceof Date){
37059             rv = this.renderDate(val);
37060         }else if(typeof val == 'boolean'){
37061             rv = this.renderBool(val);
37062         }
37063         return Roo.util.Format.htmlEncode(rv);
37064     },
37065
37066     getPropertyName : function(name){
37067         var pn = this.grid.propertyNames;
37068         return pn && pn[name] ? pn[name] : name;
37069     },
37070
37071     getCellEditor : function(colIndex, rowIndex){
37072         var p = this.store.getProperty(rowIndex);
37073         var n = p.data['name'], val = p.data['value'];
37074         
37075         if(typeof(this.grid.customEditors[n]) == 'string'){
37076             return this.editors[this.grid.customEditors[n]];
37077         }
37078         if(typeof(this.grid.customEditors[n]) != 'undefined'){
37079             return this.grid.customEditors[n];
37080         }
37081         if(val instanceof Date){
37082             return this.editors['date'];
37083         }else if(typeof val == 'number'){
37084             return this.editors['number'];
37085         }else if(typeof val == 'boolean'){
37086             return this.editors['boolean'];
37087         }else{
37088             return this.editors['string'];
37089         }
37090     }
37091 });
37092
37093 /**
37094  * @class Roo.grid.PropertyGrid
37095  * @extends Roo.grid.EditorGrid
37096  * This class represents the  interface of a component based property grid control.
37097  * <br><br>Usage:<pre><code>
37098  var grid = new Roo.grid.PropertyGrid("my-container-id", {
37099       
37100  });
37101  // set any options
37102  grid.render();
37103  * </code></pre>
37104   
37105  * @constructor
37106  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37107  * The container MUST have some type of size defined for the grid to fill. The container will be
37108  * automatically set to position relative if it isn't already.
37109  * @param {Object} config A config object that sets properties on this grid.
37110  */
37111 Roo.grid.PropertyGrid = function(container, config){
37112     config = config || {};
37113     var store = new Roo.grid.PropertyStore(this);
37114     this.store = store;
37115     var cm = new Roo.grid.PropertyColumnModel(this, store);
37116     store.store.sort('name', 'ASC');
37117     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
37118         ds: store.store,
37119         cm: cm,
37120         enableColLock:false,
37121         enableColumnMove:false,
37122         stripeRows:false,
37123         trackMouseOver: false,
37124         clicksToEdit:1
37125     }, config));
37126     this.getGridEl().addClass('x-props-grid');
37127     this.lastEditRow = null;
37128     this.on('columnresize', this.onColumnResize, this);
37129     this.addEvents({
37130          /**
37131              * @event beforepropertychange
37132              * Fires before a property changes (return false to stop?)
37133              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37134              * @param {String} id Record Id
37135              * @param {String} newval New Value
37136          * @param {String} oldval Old Value
37137              */
37138         "beforepropertychange": true,
37139         /**
37140              * @event propertychange
37141              * Fires after a property changes
37142              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37143              * @param {String} id Record Id
37144              * @param {String} newval New Value
37145          * @param {String} oldval Old Value
37146              */
37147         "propertychange": true
37148     });
37149     this.customEditors = this.customEditors || {};
37150 };
37151 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
37152     
37153      /**
37154      * @cfg {Object} customEditors map of colnames=> custom editors.
37155      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
37156      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
37157      * false disables editing of the field.
37158          */
37159     
37160       /**
37161      * @cfg {Object} propertyNames map of property Names to their displayed value
37162          */
37163     
37164     render : function(){
37165         Roo.grid.PropertyGrid.superclass.render.call(this);
37166         this.autoSize.defer(100, this);
37167     },
37168
37169     autoSize : function(){
37170         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
37171         if(this.view){
37172             this.view.fitColumns();
37173         }
37174     },
37175
37176     onColumnResize : function(){
37177         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
37178         this.autoSize();
37179     },
37180     /**
37181      * Sets the data for the Grid
37182      * accepts a Key => Value object of all the elements avaiable.
37183      * @param {Object} data  to appear in grid.
37184      */
37185     setSource : function(source){
37186         this.store.setSource(source);
37187         //this.autoSize();
37188     },
37189     /**
37190      * Gets all the data from the grid.
37191      * @return {Object} data  data stored in grid
37192      */
37193     getSource : function(){
37194         return this.store.getSource();
37195     }
37196 });/*
37197   
37198  * Licence LGPL
37199  
37200  */
37201  
37202 /**
37203  * @class Roo.grid.Calendar
37204  * @extends Roo.grid.Grid
37205  * This class extends the Grid to provide a calendar widget
37206  * <br><br>Usage:<pre><code>
37207  var grid = new Roo.grid.Calendar("my-container-id", {
37208      ds: myDataStore,
37209      cm: myColModel,
37210      selModel: mySelectionModel,
37211      autoSizeColumns: true,
37212      monitorWindowResize: false,
37213      trackMouseOver: true
37214      eventstore : real data store..
37215  });
37216  // set any options
37217  grid.render();
37218   
37219   * @constructor
37220  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37221  * The container MUST have some type of size defined for the grid to fill. The container will be
37222  * automatically set to position relative if it isn't already.
37223  * @param {Object} config A config object that sets properties on this grid.
37224  */
37225 Roo.grid.Calendar = function(container, config){
37226         // initialize the container
37227         this.container = Roo.get(container);
37228         this.container.update("");
37229         this.container.setStyle("overflow", "hidden");
37230     this.container.addClass('x-grid-container');
37231
37232     this.id = this.container.id;
37233
37234     Roo.apply(this, config);
37235     // check and correct shorthanded configs
37236     
37237     var rows = [];
37238     var d =1;
37239     for (var r = 0;r < 6;r++) {
37240         
37241         rows[r]=[];
37242         for (var c =0;c < 7;c++) {
37243             rows[r][c]= '';
37244         }
37245     }
37246     if (this.eventStore) {
37247         this.eventStore= Roo.factory(this.eventStore, Roo.data);
37248         this.eventStore.on('load',this.onLoad, this);
37249         this.eventStore.on('beforeload',this.clearEvents, this);
37250          
37251     }
37252     
37253     this.dataSource = new Roo.data.Store({
37254             proxy: new Roo.data.MemoryProxy(rows),
37255             reader: new Roo.data.ArrayReader({}, [
37256                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
37257     });
37258
37259     this.dataSource.load();
37260     this.ds = this.dataSource;
37261     this.ds.xmodule = this.xmodule || false;
37262     
37263     
37264     var cellRender = function(v,x,r)
37265     {
37266         return String.format(
37267             '<div class="fc-day  fc-widget-content"><div>' +
37268                 '<div class="fc-event-container"></div>' +
37269                 '<div class="fc-day-number">{0}</div>'+
37270                 
37271                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
37272             '</div></div>', v);
37273     
37274     }
37275     
37276     
37277     this.colModel = new Roo.grid.ColumnModel( [
37278         {
37279             xtype: 'ColumnModel',
37280             xns: Roo.grid,
37281             dataIndex : 'weekday0',
37282             header : 'Sunday',
37283             renderer : cellRender
37284         },
37285         {
37286             xtype: 'ColumnModel',
37287             xns: Roo.grid,
37288             dataIndex : 'weekday1',
37289             header : 'Monday',
37290             renderer : cellRender
37291         },
37292         {
37293             xtype: 'ColumnModel',
37294             xns: Roo.grid,
37295             dataIndex : 'weekday2',
37296             header : 'Tuesday',
37297             renderer : cellRender
37298         },
37299         {
37300             xtype: 'ColumnModel',
37301             xns: Roo.grid,
37302             dataIndex : 'weekday3',
37303             header : 'Wednesday',
37304             renderer : cellRender
37305         },
37306         {
37307             xtype: 'ColumnModel',
37308             xns: Roo.grid,
37309             dataIndex : 'weekday4',
37310             header : 'Thursday',
37311             renderer : cellRender
37312         },
37313         {
37314             xtype: 'ColumnModel',
37315             xns: Roo.grid,
37316             dataIndex : 'weekday5',
37317             header : 'Friday',
37318             renderer : cellRender
37319         },
37320         {
37321             xtype: 'ColumnModel',
37322             xns: Roo.grid,
37323             dataIndex : 'weekday6',
37324             header : 'Saturday',
37325             renderer : cellRender
37326         }
37327     ]);
37328     this.cm = this.colModel;
37329     this.cm.xmodule = this.xmodule || false;
37330  
37331         
37332           
37333     //this.selModel = new Roo.grid.CellSelectionModel();
37334     //this.sm = this.selModel;
37335     //this.selModel.init(this);
37336     
37337     
37338     if(this.width){
37339         this.container.setWidth(this.width);
37340     }
37341
37342     if(this.height){
37343         this.container.setHeight(this.height);
37344     }
37345     /** @private */
37346         this.addEvents({
37347         // raw events
37348         /**
37349          * @event click
37350          * The raw click event for the entire grid.
37351          * @param {Roo.EventObject} e
37352          */
37353         "click" : true,
37354         /**
37355          * @event dblclick
37356          * The raw dblclick event for the entire grid.
37357          * @param {Roo.EventObject} e
37358          */
37359         "dblclick" : true,
37360         /**
37361          * @event contextmenu
37362          * The raw contextmenu event for the entire grid.
37363          * @param {Roo.EventObject} e
37364          */
37365         "contextmenu" : true,
37366         /**
37367          * @event mousedown
37368          * The raw mousedown event for the entire grid.
37369          * @param {Roo.EventObject} e
37370          */
37371         "mousedown" : true,
37372         /**
37373          * @event mouseup
37374          * The raw mouseup event for the entire grid.
37375          * @param {Roo.EventObject} e
37376          */
37377         "mouseup" : true,
37378         /**
37379          * @event mouseover
37380          * The raw mouseover event for the entire grid.
37381          * @param {Roo.EventObject} e
37382          */
37383         "mouseover" : true,
37384         /**
37385          * @event mouseout
37386          * The raw mouseout event for the entire grid.
37387          * @param {Roo.EventObject} e
37388          */
37389         "mouseout" : true,
37390         /**
37391          * @event keypress
37392          * The raw keypress event for the entire grid.
37393          * @param {Roo.EventObject} e
37394          */
37395         "keypress" : true,
37396         /**
37397          * @event keydown
37398          * The raw keydown event for the entire grid.
37399          * @param {Roo.EventObject} e
37400          */
37401         "keydown" : true,
37402
37403         // custom events
37404
37405         /**
37406          * @event cellclick
37407          * Fires when a cell is clicked
37408          * @param {Grid} this
37409          * @param {Number} rowIndex
37410          * @param {Number} columnIndex
37411          * @param {Roo.EventObject} e
37412          */
37413         "cellclick" : true,
37414         /**
37415          * @event celldblclick
37416          * Fires when a cell is double clicked
37417          * @param {Grid} this
37418          * @param {Number} rowIndex
37419          * @param {Number} columnIndex
37420          * @param {Roo.EventObject} e
37421          */
37422         "celldblclick" : true,
37423         /**
37424          * @event rowclick
37425          * Fires when a row is clicked
37426          * @param {Grid} this
37427          * @param {Number} rowIndex
37428          * @param {Roo.EventObject} e
37429          */
37430         "rowclick" : true,
37431         /**
37432          * @event rowdblclick
37433          * Fires when a row is double clicked
37434          * @param {Grid} this
37435          * @param {Number} rowIndex
37436          * @param {Roo.EventObject} e
37437          */
37438         "rowdblclick" : true,
37439         /**
37440          * @event headerclick
37441          * Fires when a header is clicked
37442          * @param {Grid} this
37443          * @param {Number} columnIndex
37444          * @param {Roo.EventObject} e
37445          */
37446         "headerclick" : true,
37447         /**
37448          * @event headerdblclick
37449          * Fires when a header cell is double clicked
37450          * @param {Grid} this
37451          * @param {Number} columnIndex
37452          * @param {Roo.EventObject} e
37453          */
37454         "headerdblclick" : true,
37455         /**
37456          * @event rowcontextmenu
37457          * Fires when a row is right clicked
37458          * @param {Grid} this
37459          * @param {Number} rowIndex
37460          * @param {Roo.EventObject} e
37461          */
37462         "rowcontextmenu" : true,
37463         /**
37464          * @event cellcontextmenu
37465          * Fires when a cell is right clicked
37466          * @param {Grid} this
37467          * @param {Number} rowIndex
37468          * @param {Number} cellIndex
37469          * @param {Roo.EventObject} e
37470          */
37471          "cellcontextmenu" : true,
37472         /**
37473          * @event headercontextmenu
37474          * Fires when a header is right clicked
37475          * @param {Grid} this
37476          * @param {Number} columnIndex
37477          * @param {Roo.EventObject} e
37478          */
37479         "headercontextmenu" : true,
37480         /**
37481          * @event bodyscroll
37482          * Fires when the body element is scrolled
37483          * @param {Number} scrollLeft
37484          * @param {Number} scrollTop
37485          */
37486         "bodyscroll" : true,
37487         /**
37488          * @event columnresize
37489          * Fires when the user resizes a column
37490          * @param {Number} columnIndex
37491          * @param {Number} newSize
37492          */
37493         "columnresize" : true,
37494         /**
37495          * @event columnmove
37496          * Fires when the user moves a column
37497          * @param {Number} oldIndex
37498          * @param {Number} newIndex
37499          */
37500         "columnmove" : true,
37501         /**
37502          * @event startdrag
37503          * Fires when row(s) start being dragged
37504          * @param {Grid} this
37505          * @param {Roo.GridDD} dd The drag drop object
37506          * @param {event} e The raw browser event
37507          */
37508         "startdrag" : true,
37509         /**
37510          * @event enddrag
37511          * Fires when a drag operation is complete
37512          * @param {Grid} this
37513          * @param {Roo.GridDD} dd The drag drop object
37514          * @param {event} e The raw browser event
37515          */
37516         "enddrag" : true,
37517         /**
37518          * @event dragdrop
37519          * Fires when dragged row(s) are dropped on a valid DD target
37520          * @param {Grid} this
37521          * @param {Roo.GridDD} dd The drag drop object
37522          * @param {String} targetId The target drag drop object
37523          * @param {event} e The raw browser event
37524          */
37525         "dragdrop" : true,
37526         /**
37527          * @event dragover
37528          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
37529          * @param {Grid} this
37530          * @param {Roo.GridDD} dd The drag drop object
37531          * @param {String} targetId The target drag drop object
37532          * @param {event} e The raw browser event
37533          */
37534         "dragover" : true,
37535         /**
37536          * @event dragenter
37537          *  Fires when the dragged row(s) first cross another DD target while being dragged
37538          * @param {Grid} this
37539          * @param {Roo.GridDD} dd The drag drop object
37540          * @param {String} targetId The target drag drop object
37541          * @param {event} e The raw browser event
37542          */
37543         "dragenter" : true,
37544         /**
37545          * @event dragout
37546          * Fires when the dragged row(s) leave another DD target while being dragged
37547          * @param {Grid} this
37548          * @param {Roo.GridDD} dd The drag drop object
37549          * @param {String} targetId The target drag drop object
37550          * @param {event} e The raw browser event
37551          */
37552         "dragout" : true,
37553         /**
37554          * @event rowclass
37555          * Fires when a row is rendered, so you can change add a style to it.
37556          * @param {GridView} gridview   The grid view
37557          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
37558          */
37559         'rowclass' : true,
37560
37561         /**
37562          * @event render
37563          * Fires when the grid is rendered
37564          * @param {Grid} grid
37565          */
37566         'render' : true,
37567             /**
37568              * @event select
37569              * Fires when a date is selected
37570              * @param {DatePicker} this
37571              * @param {Date} date The selected date
37572              */
37573         'select': true,
37574         /**
37575              * @event monthchange
37576              * Fires when the displayed month changes 
37577              * @param {DatePicker} this
37578              * @param {Date} date The selected month
37579              */
37580         'monthchange': true,
37581         /**
37582              * @event evententer
37583              * Fires when mouse over an event
37584              * @param {Calendar} this
37585              * @param {event} Event
37586              */
37587         'evententer': true,
37588         /**
37589              * @event eventleave
37590              * Fires when the mouse leaves an
37591              * @param {Calendar} this
37592              * @param {event}
37593              */
37594         'eventleave': true,
37595         /**
37596              * @event eventclick
37597              * Fires when the mouse click an
37598              * @param {Calendar} this
37599              * @param {event}
37600              */
37601         'eventclick': true,
37602         /**
37603              * @event eventrender
37604              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
37605              * @param {Calendar} this
37606              * @param {data} data to be modified
37607              */
37608         'eventrender': true
37609         
37610     });
37611
37612     Roo.grid.Grid.superclass.constructor.call(this);
37613     this.on('render', function() {
37614         this.view.el.addClass('x-grid-cal'); 
37615         
37616         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
37617
37618     },this);
37619     
37620     if (!Roo.grid.Calendar.style) {
37621         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
37622             
37623             
37624             '.x-grid-cal .x-grid-col' :  {
37625                 height: 'auto !important',
37626                 'vertical-align': 'top'
37627             },
37628             '.x-grid-cal  .fc-event-hori' : {
37629                 height: '14px'
37630             }
37631              
37632             
37633         }, Roo.id());
37634     }
37635
37636     
37637     
37638 };
37639 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
37640     /**
37641      * @cfg {Store} eventStore The store that loads events.
37642      */
37643     eventStore : 25,
37644
37645      
37646     activeDate : false,
37647     startDay : 0,
37648     autoWidth : true,
37649     monitorWindowResize : false,
37650
37651     
37652     resizeColumns : function() {
37653         var col = (this.view.el.getWidth() / 7) - 3;
37654         // loop through cols, and setWidth
37655         for(var i =0 ; i < 7 ; i++){
37656             this.cm.setColumnWidth(i, col);
37657         }
37658     },
37659      setDate :function(date) {
37660         
37661         Roo.log('setDate?');
37662         
37663         this.resizeColumns();
37664         var vd = this.activeDate;
37665         this.activeDate = date;
37666 //        if(vd && this.el){
37667 //            var t = date.getTime();
37668 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37669 //                Roo.log('using add remove');
37670 //                
37671 //                this.fireEvent('monthchange', this, date);
37672 //                
37673 //                this.cells.removeClass("fc-state-highlight");
37674 //                this.cells.each(function(c){
37675 //                   if(c.dateValue == t){
37676 //                       c.addClass("fc-state-highlight");
37677 //                       setTimeout(function(){
37678 //                            try{c.dom.firstChild.focus();}catch(e){}
37679 //                       }, 50);
37680 //                       return false;
37681 //                   }
37682 //                   return true;
37683 //                });
37684 //                return;
37685 //            }
37686 //        }
37687         
37688         var days = date.getDaysInMonth();
37689         
37690         var firstOfMonth = date.getFirstDateOfMonth();
37691         var startingPos = firstOfMonth.getDay()-this.startDay;
37692         
37693         if(startingPos < this.startDay){
37694             startingPos += 7;
37695         }
37696         
37697         var pm = date.add(Date.MONTH, -1);
37698         var prevStart = pm.getDaysInMonth()-startingPos;
37699 //        
37700         
37701         
37702         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37703         
37704         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37705         //this.cells.addClassOnOver('fc-state-hover');
37706         
37707         var cells = this.cells.elements;
37708         var textEls = this.textNodes;
37709         
37710         //Roo.each(cells, function(cell){
37711         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37712         //});
37713         
37714         days += startingPos;
37715
37716         // convert everything to numbers so it's fast
37717         var day = 86400000;
37718         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37719         //Roo.log(d);
37720         //Roo.log(pm);
37721         //Roo.log(prevStart);
37722         
37723         var today = new Date().clearTime().getTime();
37724         var sel = date.clearTime().getTime();
37725         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37726         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37727         var ddMatch = this.disabledDatesRE;
37728         var ddText = this.disabledDatesText;
37729         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37730         var ddaysText = this.disabledDaysText;
37731         var format = this.format;
37732         
37733         var setCellClass = function(cal, cell){
37734             
37735             //Roo.log('set Cell Class');
37736             cell.title = "";
37737             var t = d.getTime();
37738             
37739             //Roo.log(d);
37740             
37741             
37742             cell.dateValue = t;
37743             if(t == today){
37744                 cell.className += " fc-today";
37745                 cell.className += " fc-state-highlight";
37746                 cell.title = cal.todayText;
37747             }
37748             if(t == sel){
37749                 // disable highlight in other month..
37750                 cell.className += " fc-state-highlight";
37751                 
37752             }
37753             // disabling
37754             if(t < min) {
37755                 //cell.className = " fc-state-disabled";
37756                 cell.title = cal.minText;
37757                 return;
37758             }
37759             if(t > max) {
37760                 //cell.className = " fc-state-disabled";
37761                 cell.title = cal.maxText;
37762                 return;
37763             }
37764             if(ddays){
37765                 if(ddays.indexOf(d.getDay()) != -1){
37766                     // cell.title = ddaysText;
37767                    // cell.className = " fc-state-disabled";
37768                 }
37769             }
37770             if(ddMatch && format){
37771                 var fvalue = d.dateFormat(format);
37772                 if(ddMatch.test(fvalue)){
37773                     cell.title = ddText.replace("%0", fvalue);
37774                    cell.className = " fc-state-disabled";
37775                 }
37776             }
37777             
37778             if (!cell.initialClassName) {
37779                 cell.initialClassName = cell.dom.className;
37780             }
37781             
37782             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
37783         };
37784
37785         var i = 0;
37786         
37787         for(; i < startingPos; i++) {
37788             cells[i].dayName =  (++prevStart);
37789             Roo.log(textEls[i]);
37790             d.setDate(d.getDate()+1);
37791             
37792             //cells[i].className = "fc-past fc-other-month";
37793             setCellClass(this, cells[i]);
37794         }
37795         
37796         var intDay = 0;
37797         
37798         for(; i < days; i++){
37799             intDay = i - startingPos + 1;
37800             cells[i].dayName =  (intDay);
37801             d.setDate(d.getDate()+1);
37802             
37803             cells[i].className = ''; // "x-date-active";
37804             setCellClass(this, cells[i]);
37805         }
37806         var extraDays = 0;
37807         
37808         for(; i < 42; i++) {
37809             //textEls[i].innerHTML = (++extraDays);
37810             
37811             d.setDate(d.getDate()+1);
37812             cells[i].dayName = (++extraDays);
37813             cells[i].className = "fc-future fc-other-month";
37814             setCellClass(this, cells[i]);
37815         }
37816         
37817         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37818         
37819         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37820         
37821         // this will cause all the cells to mis
37822         var rows= [];
37823         var i =0;
37824         for (var r = 0;r < 6;r++) {
37825             for (var c =0;c < 7;c++) {
37826                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37827             }    
37828         }
37829         
37830         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37831         for(i=0;i<cells.length;i++) {
37832             
37833             this.cells.elements[i].dayName = cells[i].dayName ;
37834             this.cells.elements[i].className = cells[i].className;
37835             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37836             this.cells.elements[i].title = cells[i].title ;
37837             this.cells.elements[i].dateValue = cells[i].dateValue ;
37838         }
37839         
37840         
37841         
37842         
37843         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37844         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37845         
37846         ////if(totalRows != 6){
37847             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37848            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37849        // }
37850         
37851         this.fireEvent('monthchange', this, date);
37852         
37853         
37854     },
37855  /**
37856      * Returns the grid's SelectionModel.
37857      * @return {SelectionModel}
37858      */
37859     getSelectionModel : function(){
37860         if(!this.selModel){
37861             this.selModel = new Roo.grid.CellSelectionModel();
37862         }
37863         return this.selModel;
37864     },
37865
37866     load: function() {
37867         this.eventStore.load()
37868         
37869         
37870         
37871     },
37872     
37873     findCell : function(dt) {
37874         dt = dt.clearTime().getTime();
37875         var ret = false;
37876         this.cells.each(function(c){
37877             //Roo.log("check " +c.dateValue + '?=' + dt);
37878             if(c.dateValue == dt){
37879                 ret = c;
37880                 return false;
37881             }
37882             return true;
37883         });
37884         
37885         return ret;
37886     },
37887     
37888     findCells : function(rec) {
37889         var s = rec.data.start_dt.clone().clearTime().getTime();
37890        // Roo.log(s);
37891         var e= rec.data.end_dt.clone().clearTime().getTime();
37892        // Roo.log(e);
37893         var ret = [];
37894         this.cells.each(function(c){
37895              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37896             
37897             if(c.dateValue > e){
37898                 return ;
37899             }
37900             if(c.dateValue < s){
37901                 return ;
37902             }
37903             ret.push(c);
37904         });
37905         
37906         return ret;    
37907     },
37908     
37909     findBestRow: function(cells)
37910     {
37911         var ret = 0;
37912         
37913         for (var i =0 ; i < cells.length;i++) {
37914             ret  = Math.max(cells[i].rows || 0,ret);
37915         }
37916         return ret;
37917         
37918     },
37919     
37920     
37921     addItem : function(rec)
37922     {
37923         // look for vertical location slot in
37924         var cells = this.findCells(rec);
37925         
37926         rec.row = this.findBestRow(cells);
37927         
37928         // work out the location.
37929         
37930         var crow = false;
37931         var rows = [];
37932         for(var i =0; i < cells.length; i++) {
37933             if (!crow) {
37934                 crow = {
37935                     start : cells[i],
37936                     end :  cells[i]
37937                 };
37938                 continue;
37939             }
37940             if (crow.start.getY() == cells[i].getY()) {
37941                 // on same row.
37942                 crow.end = cells[i];
37943                 continue;
37944             }
37945             // different row.
37946             rows.push(crow);
37947             crow = {
37948                 start: cells[i],
37949                 end : cells[i]
37950             };
37951             
37952         }
37953         
37954         rows.push(crow);
37955         rec.els = [];
37956         rec.rows = rows;
37957         rec.cells = cells;
37958         for (var i = 0; i < cells.length;i++) {
37959             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
37960             
37961         }
37962         
37963         
37964     },
37965     
37966     clearEvents: function() {
37967         
37968         if (!this.eventStore.getCount()) {
37969             return;
37970         }
37971         // reset number of rows in cells.
37972         Roo.each(this.cells.elements, function(c){
37973             c.rows = 0;
37974         });
37975         
37976         this.eventStore.each(function(e) {
37977             this.clearEvent(e);
37978         },this);
37979         
37980     },
37981     
37982     clearEvent : function(ev)
37983     {
37984         if (ev.els) {
37985             Roo.each(ev.els, function(el) {
37986                 el.un('mouseenter' ,this.onEventEnter, this);
37987                 el.un('mouseleave' ,this.onEventLeave, this);
37988                 el.remove();
37989             },this);
37990             ev.els = [];
37991         }
37992     },
37993     
37994     
37995     renderEvent : function(ev,ctr) {
37996         if (!ctr) {
37997              ctr = this.view.el.select('.fc-event-container',true).first();
37998         }
37999         
38000          
38001         this.clearEvent(ev);
38002             //code
38003        
38004         
38005         
38006         ev.els = [];
38007         var cells = ev.cells;
38008         var rows = ev.rows;
38009         this.fireEvent('eventrender', this, ev);
38010         
38011         for(var i =0; i < rows.length; i++) {
38012             
38013             cls = '';
38014             if (i == 0) {
38015                 cls += ' fc-event-start';
38016             }
38017             if ((i+1) == rows.length) {
38018                 cls += ' fc-event-end';
38019             }
38020             
38021             //Roo.log(ev.data);
38022             // how many rows should it span..
38023             var cg = this.eventTmpl.append(ctr,Roo.apply({
38024                 fccls : cls
38025                 
38026             }, ev.data) , true);
38027             
38028             
38029             cg.on('mouseenter' ,this.onEventEnter, this, ev);
38030             cg.on('mouseleave' ,this.onEventLeave, this, ev);
38031             cg.on('click', this.onEventClick, this, ev);
38032             
38033             ev.els.push(cg);
38034             
38035             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
38036             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
38037             //Roo.log(cg);
38038              
38039             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
38040             cg.setWidth(ebox.right - sbox.x -2);
38041         }
38042     },
38043     
38044     renderEvents: function()
38045     {   
38046         // first make sure there is enough space..
38047         
38048         if (!this.eventTmpl) {
38049             this.eventTmpl = new Roo.Template(
38050                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
38051                     '<div class="fc-event-inner">' +
38052                         '<span class="fc-event-time">{time}</span>' +
38053                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
38054                     '</div>' +
38055                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
38056                 '</div>'
38057             );
38058                 
38059         }
38060                
38061         
38062         
38063         this.cells.each(function(c) {
38064             //Roo.log(c.select('.fc-day-content div',true).first());
38065             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
38066         });
38067         
38068         var ctr = this.view.el.select('.fc-event-container',true).first();
38069         
38070         var cls;
38071         this.eventStore.each(function(ev){
38072             
38073             this.renderEvent(ev);
38074              
38075              
38076         }, this);
38077         this.view.layout();
38078         
38079     },
38080     
38081     onEventEnter: function (e, el,event,d) {
38082         this.fireEvent('evententer', this, el, event);
38083     },
38084     
38085     onEventLeave: function (e, el,event,d) {
38086         this.fireEvent('eventleave', this, el, event);
38087     },
38088     
38089     onEventClick: function (e, el,event,d) {
38090         this.fireEvent('eventclick', this, el, event);
38091     },
38092     
38093     onMonthChange: function () {
38094         this.store.load();
38095     },
38096     
38097     onLoad: function () {
38098         
38099         //Roo.log('calendar onload');
38100 //         
38101         if(this.eventStore.getCount() > 0){
38102             
38103            
38104             
38105             this.eventStore.each(function(d){
38106                 
38107                 
38108                 // FIXME..
38109                 var add =   d.data;
38110                 if (typeof(add.end_dt) == 'undefined')  {
38111                     Roo.log("Missing End time in calendar data: ");
38112                     Roo.log(d);
38113                     return;
38114                 }
38115                 if (typeof(add.start_dt) == 'undefined')  {
38116                     Roo.log("Missing Start time in calendar data: ");
38117                     Roo.log(d);
38118                     return;
38119                 }
38120                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
38121                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
38122                 add.id = add.id || d.id;
38123                 add.title = add.title || '??';
38124                 
38125                 this.addItem(d);
38126                 
38127              
38128             },this);
38129         }
38130         
38131         this.renderEvents();
38132     }
38133     
38134
38135 });
38136 /*
38137  grid : {
38138                 xtype: 'Grid',
38139                 xns: Roo.grid,
38140                 listeners : {
38141                     render : function ()
38142                     {
38143                         _this.grid = this;
38144                         
38145                         if (!this.view.el.hasClass('course-timesheet')) {
38146                             this.view.el.addClass('course-timesheet');
38147                         }
38148                         if (this.tsStyle) {
38149                             this.ds.load({});
38150                             return; 
38151                         }
38152                         Roo.log('width');
38153                         Roo.log(_this.grid.view.el.getWidth());
38154                         
38155                         
38156                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
38157                             '.course-timesheet .x-grid-row' : {
38158                                 height: '80px'
38159                             },
38160                             '.x-grid-row td' : {
38161                                 'vertical-align' : 0
38162                             },
38163                             '.course-edit-link' : {
38164                                 'color' : 'blue',
38165                                 'text-overflow' : 'ellipsis',
38166                                 'overflow' : 'hidden',
38167                                 'white-space' : 'nowrap',
38168                                 'cursor' : 'pointer'
38169                             },
38170                             '.sub-link' : {
38171                                 'color' : 'green'
38172                             },
38173                             '.de-act-sup-link' : {
38174                                 'color' : 'purple',
38175                                 'text-decoration' : 'line-through'
38176                             },
38177                             '.de-act-link' : {
38178                                 'color' : 'red',
38179                                 'text-decoration' : 'line-through'
38180                             },
38181                             '.course-timesheet .course-highlight' : {
38182                                 'border-top-style': 'dashed !important',
38183                                 'border-bottom-bottom': 'dashed !important'
38184                             },
38185                             '.course-timesheet .course-item' : {
38186                                 'font-family'   : 'tahoma, arial, helvetica',
38187                                 'font-size'     : '11px',
38188                                 'overflow'      : 'hidden',
38189                                 'padding-left'  : '10px',
38190                                 'padding-right' : '10px',
38191                                 'padding-top' : '10px' 
38192                             }
38193                             
38194                         }, Roo.id());
38195                                 this.ds.load({});
38196                     }
38197                 },
38198                 autoWidth : true,
38199                 monitorWindowResize : false,
38200                 cellrenderer : function(v,x,r)
38201                 {
38202                     return v;
38203                 },
38204                 sm : {
38205                     xtype: 'CellSelectionModel',
38206                     xns: Roo.grid
38207                 },
38208                 dataSource : {
38209                     xtype: 'Store',
38210                     xns: Roo.data,
38211                     listeners : {
38212                         beforeload : function (_self, options)
38213                         {
38214                             options.params = options.params || {};
38215                             options.params._month = _this.monthField.getValue();
38216                             options.params.limit = 9999;
38217                             options.params['sort'] = 'when_dt';    
38218                             options.params['dir'] = 'ASC';    
38219                             this.proxy.loadResponse = this.loadResponse;
38220                             Roo.log("load?");
38221                             //this.addColumns();
38222                         },
38223                         load : function (_self, records, options)
38224                         {
38225                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
38226                                 // if you click on the translation.. you can edit it...
38227                                 var el = Roo.get(this);
38228                                 var id = el.dom.getAttribute('data-id');
38229                                 var d = el.dom.getAttribute('data-date');
38230                                 var t = el.dom.getAttribute('data-time');
38231                                 //var id = this.child('span').dom.textContent;
38232                                 
38233                                 //Roo.log(this);
38234                                 Pman.Dialog.CourseCalendar.show({
38235                                     id : id,
38236                                     when_d : d,
38237                                     when_t : t,
38238                                     productitem_active : id ? 1 : 0
38239                                 }, function() {
38240                                     _this.grid.ds.load({});
38241                                 });
38242                            
38243                            });
38244                            
38245                            _this.panel.fireEvent('resize', [ '', '' ]);
38246                         }
38247                     },
38248                     loadResponse : function(o, success, response){
38249                             // this is overridden on before load..
38250                             
38251                             Roo.log("our code?");       
38252                             //Roo.log(success);
38253                             //Roo.log(response)
38254                             delete this.activeRequest;
38255                             if(!success){
38256                                 this.fireEvent("loadexception", this, o, response);
38257                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38258                                 return;
38259                             }
38260                             var result;
38261                             try {
38262                                 result = o.reader.read(response);
38263                             }catch(e){
38264                                 Roo.log("load exception?");
38265                                 this.fireEvent("loadexception", this, o, response, e);
38266                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38267                                 return;
38268                             }
38269                             Roo.log("ready...");        
38270                             // loop through result.records;
38271                             // and set this.tdate[date] = [] << array of records..
38272                             _this.tdata  = {};
38273                             Roo.each(result.records, function(r){
38274                                 //Roo.log(r.data);
38275                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
38276                                     _this.tdata[r.data.when_dt.format('j')] = [];
38277                                 }
38278                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
38279                             });
38280                             
38281                             //Roo.log(_this.tdata);
38282                             
38283                             result.records = [];
38284                             result.totalRecords = 6;
38285                     
38286                             // let's generate some duumy records for the rows.
38287                             //var st = _this.dateField.getValue();
38288                             
38289                             // work out monday..
38290                             //st = st.add(Date.DAY, -1 * st.format('w'));
38291                             
38292                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38293                             
38294                             var firstOfMonth = date.getFirstDayOfMonth();
38295                             var days = date.getDaysInMonth();
38296                             var d = 1;
38297                             var firstAdded = false;
38298                             for (var i = 0; i < result.totalRecords ; i++) {
38299                                 //var d= st.add(Date.DAY, i);
38300                                 var row = {};
38301                                 var added = 0;
38302                                 for(var w = 0 ; w < 7 ; w++){
38303                                     if(!firstAdded && firstOfMonth != w){
38304                                         continue;
38305                                     }
38306                                     if(d > days){
38307                                         continue;
38308                                     }
38309                                     firstAdded = true;
38310                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
38311                                     row['weekday'+w] = String.format(
38312                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
38313                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
38314                                                     d,
38315                                                     date.format('Y-m-')+dd
38316                                                 );
38317                                     added++;
38318                                     if(typeof(_this.tdata[d]) != 'undefined'){
38319                                         Roo.each(_this.tdata[d], function(r){
38320                                             var is_sub = '';
38321                                             var deactive = '';
38322                                             var id = r.id;
38323                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
38324                                             if(r.parent_id*1>0){
38325                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
38326                                                 id = r.parent_id;
38327                                             }
38328                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
38329                                                 deactive = 'de-act-link';
38330                                             }
38331                                             
38332                                             row['weekday'+w] += String.format(
38333                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
38334                                                     id, //0
38335                                                     r.product_id_name, //1
38336                                                     r.when_dt.format('h:ia'), //2
38337                                                     is_sub, //3
38338                                                     deactive, //4
38339                                                     desc // 5
38340                                             );
38341                                         });
38342                                     }
38343                                     d++;
38344                                 }
38345                                 
38346                                 // only do this if something added..
38347                                 if(added > 0){ 
38348                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
38349                                 }
38350                                 
38351                                 
38352                                 // push it twice. (second one with an hour..
38353                                 
38354                             }
38355                             //Roo.log(result);
38356                             this.fireEvent("load", this, o, o.request.arg);
38357                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
38358                         },
38359                     sortInfo : {field: 'when_dt', direction : 'ASC' },
38360                     proxy : {
38361                         xtype: 'HttpProxy',
38362                         xns: Roo.data,
38363                         method : 'GET',
38364                         url : baseURL + '/Roo/Shop_course.php'
38365                     },
38366                     reader : {
38367                         xtype: 'JsonReader',
38368                         xns: Roo.data,
38369                         id : 'id',
38370                         fields : [
38371                             {
38372                                 'name': 'id',
38373                                 'type': 'int'
38374                             },
38375                             {
38376                                 'name': 'when_dt',
38377                                 'type': 'string'
38378                             },
38379                             {
38380                                 'name': 'end_dt',
38381                                 'type': 'string'
38382                             },
38383                             {
38384                                 'name': 'parent_id',
38385                                 'type': 'int'
38386                             },
38387                             {
38388                                 'name': 'product_id',
38389                                 'type': 'int'
38390                             },
38391                             {
38392                                 'name': 'productitem_id',
38393                                 'type': 'int'
38394                             },
38395                             {
38396                                 'name': 'guid',
38397                                 'type': 'int'
38398                             }
38399                         ]
38400                     }
38401                 },
38402                 toolbar : {
38403                     xtype: 'Toolbar',
38404                     xns: Roo,
38405                     items : [
38406                         {
38407                             xtype: 'Button',
38408                             xns: Roo.Toolbar,
38409                             listeners : {
38410                                 click : function (_self, e)
38411                                 {
38412                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38413                                     sd.setMonth(sd.getMonth()-1);
38414                                     _this.monthField.setValue(sd.format('Y-m-d'));
38415                                     _this.grid.ds.load({});
38416                                 }
38417                             },
38418                             text : "Back"
38419                         },
38420                         {
38421                             xtype: 'Separator',
38422                             xns: Roo.Toolbar
38423                         },
38424                         {
38425                             xtype: 'MonthField',
38426                             xns: Roo.form,
38427                             listeners : {
38428                                 render : function (_self)
38429                                 {
38430                                     _this.monthField = _self;
38431                                    // _this.monthField.set  today
38432                                 },
38433                                 select : function (combo, date)
38434                                 {
38435                                     _this.grid.ds.load({});
38436                                 }
38437                             },
38438                             value : (function() { return new Date(); })()
38439                         },
38440                         {
38441                             xtype: 'Separator',
38442                             xns: Roo.Toolbar
38443                         },
38444                         {
38445                             xtype: 'TextItem',
38446                             xns: Roo.Toolbar,
38447                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
38448                         },
38449                         {
38450                             xtype: 'Fill',
38451                             xns: Roo.Toolbar
38452                         },
38453                         {
38454                             xtype: 'Button',
38455                             xns: Roo.Toolbar,
38456                             listeners : {
38457                                 click : function (_self, e)
38458                                 {
38459                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38460                                     sd.setMonth(sd.getMonth()+1);
38461                                     _this.monthField.setValue(sd.format('Y-m-d'));
38462                                     _this.grid.ds.load({});
38463                                 }
38464                             },
38465                             text : "Next"
38466                         }
38467                     ]
38468                 },
38469                  
38470             }
38471         };
38472         
38473         *//*
38474  * Based on:
38475  * Ext JS Library 1.1.1
38476  * Copyright(c) 2006-2007, Ext JS, LLC.
38477  *
38478  * Originally Released Under LGPL - original licence link has changed is not relivant.
38479  *
38480  * Fork - LGPL
38481  * <script type="text/javascript">
38482  */
38483  
38484 /**
38485  * @class Roo.LoadMask
38486  * A simple utility class for generically masking elements while loading data.  If the element being masked has
38487  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38488  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
38489  * element's UpdateManager load indicator and will be destroyed after the initial load.
38490  * @constructor
38491  * Create a new LoadMask
38492  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38493  * @param {Object} config The config object
38494  */
38495 Roo.LoadMask = function(el, config){
38496     this.el = Roo.get(el);
38497     Roo.apply(this, config);
38498     if(this.store){
38499         this.store.on('beforeload', this.onBeforeLoad, this);
38500         this.store.on('load', this.onLoad, this);
38501         this.store.on('loadexception', this.onLoadException, this);
38502         this.removeMask = false;
38503     }else{
38504         var um = this.el.getUpdateManager();
38505         um.showLoadIndicator = false; // disable the default indicator
38506         um.on('beforeupdate', this.onBeforeLoad, this);
38507         um.on('update', this.onLoad, this);
38508         um.on('failure', this.onLoad, this);
38509         this.removeMask = true;
38510     }
38511 };
38512
38513 Roo.LoadMask.prototype = {
38514     /**
38515      * @cfg {Boolean} removeMask
38516      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38517      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
38518      */
38519     removeMask : false,
38520     /**
38521      * @cfg {String} msg
38522      * The text to display in a centered loading message box (defaults to 'Loading...')
38523      */
38524     msg : 'Loading...',
38525     /**
38526      * @cfg {String} msgCls
38527      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38528      */
38529     msgCls : 'x-mask-loading',
38530
38531     /**
38532      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38533      * @type Boolean
38534      */
38535     disabled: false,
38536
38537     /**
38538      * Disables the mask to prevent it from being displayed
38539      */
38540     disable : function(){
38541        this.disabled = true;
38542     },
38543
38544     /**
38545      * Enables the mask so that it can be displayed
38546      */
38547     enable : function(){
38548         this.disabled = false;
38549     },
38550     
38551     onLoadException : function()
38552     {
38553         Roo.log(arguments);
38554         
38555         if (typeof(arguments[3]) != 'undefined') {
38556             Roo.MessageBox.alert("Error loading",arguments[3]);
38557         } 
38558         /*
38559         try {
38560             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38561                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38562             }   
38563         } catch(e) {
38564             
38565         }
38566         */
38567     
38568         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38569     },
38570     // private
38571     onLoad : function()
38572     {
38573         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38574     },
38575
38576     // private
38577     onBeforeLoad : function(){
38578         if(!this.disabled){
38579             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
38580         }
38581     },
38582
38583     // private
38584     destroy : function(){
38585         if(this.store){
38586             this.store.un('beforeload', this.onBeforeLoad, this);
38587             this.store.un('load', this.onLoad, this);
38588             this.store.un('loadexception', this.onLoadException, this);
38589         }else{
38590             var um = this.el.getUpdateManager();
38591             um.un('beforeupdate', this.onBeforeLoad, this);
38592             um.un('update', this.onLoad, this);
38593             um.un('failure', this.onLoad, this);
38594         }
38595     }
38596 };/*
38597  * Based on:
38598  * Ext JS Library 1.1.1
38599  * Copyright(c) 2006-2007, Ext JS, LLC.
38600  *
38601  * Originally Released Under LGPL - original licence link has changed is not relivant.
38602  *
38603  * Fork - LGPL
38604  * <script type="text/javascript">
38605  */
38606
38607
38608 /**
38609  * @class Roo.XTemplate
38610  * @extends Roo.Template
38611  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38612 <pre><code>
38613 var t = new Roo.XTemplate(
38614         '&lt;select name="{name}"&gt;',
38615                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
38616         '&lt;/select&gt;'
38617 );
38618  
38619 // then append, applying the master template values
38620  </code></pre>
38621  *
38622  * Supported features:
38623  *
38624  *  Tags:
38625
38626 <pre><code>
38627       {a_variable} - output encoded.
38628       {a_variable.format:("Y-m-d")} - call a method on the variable
38629       {a_variable:raw} - unencoded output
38630       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38631       {a_variable:this.method_on_template(...)} - call a method on the template object.
38632  
38633 </code></pre>
38634  *  The tpl tag:
38635 <pre><code>
38636         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
38637         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
38638         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
38639         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
38640   
38641         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
38642         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
38643 </code></pre>
38644  *      
38645  */
38646 Roo.XTemplate = function()
38647 {
38648     Roo.XTemplate.superclass.constructor.apply(this, arguments);
38649     if (this.html) {
38650         this.compile();
38651     }
38652 };
38653
38654
38655 Roo.extend(Roo.XTemplate, Roo.Template, {
38656
38657     /**
38658      * The various sub templates
38659      */
38660     tpls : false,
38661     /**
38662      *
38663      * basic tag replacing syntax
38664      * WORD:WORD()
38665      *
38666      * // you can fake an object call by doing this
38667      *  x.t:(test,tesT) 
38668      * 
38669      */
38670     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38671
38672     /**
38673      * compile the template
38674      *
38675      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38676      *
38677      */
38678     compile: function()
38679     {
38680         var s = this.html;
38681      
38682         s = ['<tpl>', s, '</tpl>'].join('');
38683     
38684         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38685             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38686             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38687             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38688             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38689             m,
38690             id     = 0,
38691             tpls   = [];
38692     
38693         while(true == !!(m = s.match(re))){
38694             var forMatch   = m[0].match(nameRe),
38695                 ifMatch   = m[0].match(ifRe),
38696                 execMatch   = m[0].match(execRe),
38697                 namedMatch   = m[0].match(namedRe),
38698                 
38699                 exp  = null, 
38700                 fn   = null,
38701                 exec = null,
38702                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38703                 
38704             if (ifMatch) {
38705                 // if - puts fn into test..
38706                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38707                 if(exp){
38708                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38709                 }
38710             }
38711             
38712             if (execMatch) {
38713                 // exec - calls a function... returns empty if true is  returned.
38714                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38715                 if(exp){
38716                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38717                 }
38718             }
38719             
38720             
38721             if (name) {
38722                 // for = 
38723                 switch(name){
38724                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38725                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38726                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38727                 }
38728             }
38729             var uid = namedMatch ? namedMatch[1] : id;
38730             
38731             
38732             tpls.push({
38733                 id:     namedMatch ? namedMatch[1] : id,
38734                 target: name,
38735                 exec:   exec,
38736                 test:   fn,
38737                 body:   m[1] || ''
38738             });
38739             if (namedMatch) {
38740                 s = s.replace(m[0], '');
38741             } else { 
38742                 s = s.replace(m[0], '{xtpl'+ id + '}');
38743             }
38744             ++id;
38745         }
38746         this.tpls = [];
38747         for(var i = tpls.length-1; i >= 0; --i){
38748             this.compileTpl(tpls[i]);
38749             this.tpls[tpls[i].id] = tpls[i];
38750         }
38751         this.master = tpls[tpls.length-1];
38752         return this;
38753     },
38754     /**
38755      * same as applyTemplate, except it's done to one of the subTemplates
38756      * when using named templates, you can do:
38757      *
38758      * var str = pl.applySubTemplate('your-name', values);
38759      *
38760      * 
38761      * @param {Number} id of the template
38762      * @param {Object} values to apply to template
38763      * @param {Object} parent (normaly the instance of this object)
38764      */
38765     applySubTemplate : function(id, values, parent)
38766     {
38767         
38768         
38769         var t = this.tpls[id];
38770         
38771         
38772         try { 
38773             if(t.test && !t.test.call(this, values, parent)){
38774                 return '';
38775             }
38776         } catch(e) {
38777             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38778             Roo.log(e.toString());
38779             Roo.log(t.test);
38780             return ''
38781         }
38782         try { 
38783             
38784             if(t.exec && t.exec.call(this, values, parent)){
38785                 return '';
38786             }
38787         } catch(e) {
38788             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38789             Roo.log(e.toString());
38790             Roo.log(t.exec);
38791             return ''
38792         }
38793         try {
38794             var vs = t.target ? t.target.call(this, values, parent) : values;
38795             parent = t.target ? values : parent;
38796             if(t.target && vs instanceof Array){
38797                 var buf = [];
38798                 for(var i = 0, len = vs.length; i < len; i++){
38799                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38800                 }
38801                 return buf.join('');
38802             }
38803             return t.compiled.call(this, vs, parent);
38804         } catch (e) {
38805             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38806             Roo.log(e.toString());
38807             Roo.log(t.compiled);
38808             return '';
38809         }
38810     },
38811
38812     compileTpl : function(tpl)
38813     {
38814         var fm = Roo.util.Format;
38815         var useF = this.disableFormats !== true;
38816         var sep = Roo.isGecko ? "+" : ",";
38817         var undef = function(str) {
38818             Roo.log("Property not found :"  + str);
38819             return '';
38820         };
38821         
38822         var fn = function(m, name, format, args)
38823         {
38824             //Roo.log(arguments);
38825             args = args ? args.replace(/\\'/g,"'") : args;
38826             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38827             if (typeof(format) == 'undefined') {
38828                 format= 'htmlEncode';
38829             }
38830             if (format == 'raw' ) {
38831                 format = false;
38832             }
38833             
38834             if(name.substr(0, 4) == 'xtpl'){
38835                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38836             }
38837             
38838             // build an array of options to determine if value is undefined..
38839             
38840             // basically get 'xxxx.yyyy' then do
38841             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38842             //    (function () { Roo.log("Property not found"); return ''; })() :
38843             //    ......
38844             
38845             var udef_ar = [];
38846             var lookfor = '';
38847             Roo.each(name.split('.'), function(st) {
38848                 lookfor += (lookfor.length ? '.': '') + st;
38849                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38850             });
38851             
38852             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38853             
38854             
38855             if(format && useF){
38856                 
38857                 args = args ? ',' + args : "";
38858                  
38859                 if(format.substr(0, 5) != "this."){
38860                     format = "fm." + format + '(';
38861                 }else{
38862                     format = 'this.call("'+ format.substr(5) + '", ';
38863                     args = ", values";
38864                 }
38865                 
38866                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38867             }
38868              
38869             if (args.length) {
38870                 // called with xxyx.yuu:(test,test)
38871                 // change to ()
38872                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38873             }
38874             // raw.. - :raw modifier..
38875             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38876             
38877         };
38878         var body;
38879         // branched to use + in gecko and [].join() in others
38880         if(Roo.isGecko){
38881             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38882                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38883                     "';};};";
38884         }else{
38885             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38886             body.push(tpl.body.replace(/(\r\n|\n)/g,
38887                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38888             body.push("'].join('');};};");
38889             body = body.join('');
38890         }
38891         
38892         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38893        
38894         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38895         eval(body);
38896         
38897         return this;
38898     },
38899
38900     applyTemplate : function(values){
38901         return this.master.compiled.call(this, values, {});
38902         //var s = this.subs;
38903     },
38904
38905     apply : function(){
38906         return this.applyTemplate.apply(this, arguments);
38907     }
38908
38909  });
38910
38911 Roo.XTemplate.from = function(el){
38912     el = Roo.getDom(el);
38913     return new Roo.XTemplate(el.value || el.innerHTML);
38914 };