major doc changes
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13 /**
14  * @class Roo.data.SortTypes
15  * @singleton
16  * Defines the default sorting (casting?) comparison functions used when sorting data.
17  */
18 Roo.data.SortTypes = {
19     /**
20      * Default sort that does nothing
21      * @param {Mixed} s The value being converted
22      * @return {Mixed} The comparison value
23      */
24     none : function(s){
25         return s;
26     },
27     
28     /**
29      * The regular expression used to strip tags
30      * @type {RegExp}
31      * @property
32      */
33     stripTagsRE : /<\/?[^>]+>/gi,
34     
35     /**
36      * Strips all HTML tags to sort on text only
37      * @param {Mixed} s The value being converted
38      * @return {String} The comparison value
39      */
40     asText : function(s){
41         return String(s).replace(this.stripTagsRE, "");
42     },
43     
44     /**
45      * Strips all HTML tags to sort on text only - Case insensitive
46      * @param {Mixed} s The value being converted
47      * @return {String} The comparison value
48      */
49     asUCText : function(s){
50         return String(s).toUpperCase().replace(this.stripTagsRE, "");
51     },
52     
53     /**
54      * Case insensitive string
55      * @param {Mixed} s The value being converted
56      * @return {String} The comparison value
57      */
58     asUCString : function(s) {
59         return String(s).toUpperCase();
60     },
61     
62     /**
63      * Date sorting
64      * @param {Mixed} s The value being converted
65      * @return {Number} The comparison value
66      */
67     asDate : function(s) {
68         if(!s){
69             return 0;
70         }
71         if(s instanceof Date){
72             return s.getTime();
73         }
74         return Date.parse(String(s));
75     },
76     
77     /**
78      * Float sorting
79      * @param {Mixed} s The value being converted
80      * @return {Float} The comparison value
81      */
82     asFloat : function(s) {
83         var val = parseFloat(String(s).replace(/,/g, ""));
84         if(isNaN(val)) {
85             val = 0;
86         }
87         return val;
88     },
89     
90     /**
91      * Integer sorting
92      * @param {Mixed} s The value being converted
93      * @return {Number} The comparison value
94      */
95     asInt : function(s) {
96         var val = parseInt(String(s).replace(/,/g, ""));
97         if(isNaN(val)) {
98             val = 0;
99         }
100         return val;
101     }
102 };/*
103  * Based on:
104  * Ext JS Library 1.1.1
105  * Copyright(c) 2006-2007, Ext JS, LLC.
106  *
107  * Originally Released Under LGPL - original licence link has changed is not relivant.
108  *
109  * Fork - LGPL
110  * <script type="text/javascript">
111  */
112
113 /**
114 * @class Roo.data.Record
115  * Instances of this class encapsulate both record <em>definition</em> information, and record
116  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
117  * to access Records cached in an {@link Roo.data.Store} object.<br>
118  * <p>
119  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
120  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
121  * objects.<br>
122  * <p>
123  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
124  * @constructor
125  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
126  * {@link #create}. The parameters are the same.
127  * @param {Array} data An associative Array of data values keyed by the field name.
128  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
129  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
130  * not specified an integer id is generated.
131  */
132 Roo.data.Record = function(data, id){
133     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
134     this.data = data;
135 };
136
137 /**
138  * Generate a constructor for a specific record layout.
139  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
140  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
141  * Each field definition object may contain the following properties: <ul>
142  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
143  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
144  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
145  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
146  * is being used, then this is a string containing the javascript expression to reference the data relative to 
147  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
148  * to the data item relative to the record element. If the mapping expression is the same as the field name,
149  * this may be omitted.</p></li>
150  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
151  * <ul><li>auto (Default, implies no conversion)</li>
152  * <li>string</li>
153  * <li>int</li>
154  * <li>float</li>
155  * <li>boolean</li>
156  * <li>date</li></ul></p></li>
157  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
158  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
159  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
160  * by the Reader into an object that will be stored in the Record. It is passed the
161  * following parameters:<ul>
162  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
163  * </ul></p></li>
164  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
165  * </ul>
166  * <br>usage:<br><pre><code>
167 var TopicRecord = Roo.data.Record.create(
168     {name: 'title', mapping: 'topic_title'},
169     {name: 'author', mapping: 'username'},
170     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
171     {name: 'lastPost', mapping: 'post_time', type: 'date'},
172     {name: 'lastPoster', mapping: 'user2'},
173     {name: 'excerpt', mapping: 'post_text'}
174 );
175
176 var myNewRecord = new TopicRecord({
177     title: 'Do my job please',
178     author: 'noobie',
179     totalPosts: 1,
180     lastPost: new Date(),
181     lastPoster: 'Animal',
182     excerpt: 'No way dude!'
183 });
184 myStore.add(myNewRecord);
185 </code></pre>
186  * @method create
187  * @static
188  */
189 Roo.data.Record.create = function(o){
190     var f = function(){
191         f.superclass.constructor.apply(this, arguments);
192     };
193     Roo.extend(f, Roo.data.Record);
194     var p = f.prototype;
195     p.fields = new Roo.util.MixedCollection(false, function(field){
196         return field.name;
197     });
198     for(var i = 0, len = o.length; i < len; i++){
199         p.fields.add(new Roo.data.Field(o[i]));
200     }
201     f.getField = function(name){
202         return p.fields.get(name);  
203     };
204     return f;
205 };
206
207 Roo.data.Record.AUTO_ID = 1000;
208 Roo.data.Record.EDIT = 'edit';
209 Roo.data.Record.REJECT = 'reject';
210 Roo.data.Record.COMMIT = 'commit';
211
212 Roo.data.Record.prototype = {
213     /**
214      * Readonly flag - true if this record has been modified.
215      * @type Boolean
216      */
217     dirty : false,
218     editing : false,
219     error: null,
220     modified: null,
221
222     // private
223     join : function(store){
224         this.store = store;
225     },
226
227     /**
228      * Set the named field to the specified value.
229      * @param {String} name The name of the field to set.
230      * @param {Object} value The value to set the field to.
231      */
232     set : function(name, value){
233         if(this.data[name] == value){
234             return;
235         }
236         this.dirty = true;
237         if(!this.modified){
238             this.modified = {};
239         }
240         if(typeof this.modified[name] == 'undefined'){
241             this.modified[name] = this.data[name];
242         }
243         this.data[name] = value;
244         if(!this.editing && this.store){
245             this.store.afterEdit(this);
246         }       
247     },
248
249     /**
250      * Get the value of the named field.
251      * @param {String} name The name of the field to get the value of.
252      * @return {Object} The value of the field.
253      */
254     get : function(name){
255         return this.data[name]; 
256     },
257
258     // private
259     beginEdit : function(){
260         this.editing = true;
261         this.modified = {}; 
262     },
263
264     // private
265     cancelEdit : function(){
266         this.editing = false;
267         delete this.modified;
268     },
269
270     // private
271     endEdit : function(){
272         this.editing = false;
273         if(this.dirty && this.store){
274             this.store.afterEdit(this);
275         }
276     },
277
278     /**
279      * Usually called by the {@link Roo.data.Store} which owns the Record.
280      * Rejects all changes made to the Record since either creation, or the last commit operation.
281      * Modified fields are reverted to their original values.
282      * <p>
283      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
284      * of reject operations.
285      */
286     reject : function(){
287         var m = this.modified;
288         for(var n in m){
289             if(typeof m[n] != "function"){
290                 this.data[n] = m[n];
291             }
292         }
293         this.dirty = false;
294         delete this.modified;
295         this.editing = false;
296         if(this.store){
297             this.store.afterReject(this);
298         }
299     },
300
301     /**
302      * Usually called by the {@link Roo.data.Store} which owns the Record.
303      * Commits all changes made to the Record since either creation, or the last commit operation.
304      * <p>
305      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
306      * of commit operations.
307      */
308     commit : function(){
309         this.dirty = false;
310         delete this.modified;
311         this.editing = false;
312         if(this.store){
313             this.store.afterCommit(this);
314         }
315     },
316
317     // private
318     hasError : function(){
319         return this.error != null;
320     },
321
322     // private
323     clearError : function(){
324         this.error = null;
325     },
326
327     /**
328      * Creates a copy of this record.
329      * @param {String} id (optional) A new record id if you don't want to use this record's id
330      * @return {Record}
331      */
332     copy : function(newId) {
333         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
334     }
335 };/*
336  * Based on:
337  * Ext JS Library 1.1.1
338  * Copyright(c) 2006-2007, Ext JS, LLC.
339  *
340  * Originally Released Under LGPL - original licence link has changed is not relivant.
341  *
342  * Fork - LGPL
343  * <script type="text/javascript">
344  */
345
346
347
348 /**
349  * @class Roo.data.Store
350  * @extends Roo.util.Observable
351  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
352  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
353  * <p>
354  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
355  * has no knowledge of the format of the data returned by the Proxy.<br>
356  * <p>
357  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
358  * instances from the data object. These records are cached and made available through accessor functions.
359  * @constructor
360  * Creates a new Store.
361  * @param {Object} config A config object containing the objects needed for the Store to access data,
362  * and read the data into Records.
363  */
364 Roo.data.Store = function(config){
365     this.data = new Roo.util.MixedCollection(false);
366     this.data.getKey = function(o){
367         return o.id;
368     };
369     this.baseParams = {};
370     // private
371     this.paramNames = {
372         "start" : "start",
373         "limit" : "limit",
374         "sort" : "sort",
375         "dir" : "dir",
376         "multisort" : "_multisort"
377     };
378
379     if(config && config.data){
380         this.inlineData = config.data;
381         delete config.data;
382     }
383
384     Roo.apply(this, config);
385     
386     if(this.reader){ // reader passed
387         this.reader = Roo.factory(this.reader, Roo.data);
388         this.reader.xmodule = this.xmodule || false;
389         if(!this.recordType){
390             this.recordType = this.reader.recordType;
391         }
392         if(this.reader.onMetaChange){
393             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
394         }
395     }
396
397     if(this.recordType){
398         this.fields = this.recordType.prototype.fields;
399     }
400     this.modified = [];
401
402     this.addEvents({
403         /**
404          * @event datachanged
405          * Fires when the data cache has changed, and a widget which is using this Store
406          * as a Record cache should refresh its view.
407          * @param {Store} this
408          */
409         datachanged : true,
410         /**
411          * @event metachange
412          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
413          * @param {Store} this
414          * @param {Object} meta The JSON metadata
415          */
416         metachange : true,
417         /**
418          * @event add
419          * Fires when Records have been added to the Store
420          * @param {Store} this
421          * @param {Roo.data.Record[]} records The array of Records added
422          * @param {Number} index The index at which the record(s) were added
423          */
424         add : true,
425         /**
426          * @event remove
427          * Fires when a Record has been removed from the Store
428          * @param {Store} this
429          * @param {Roo.data.Record} record The Record that was removed
430          * @param {Number} index The index at which the record was removed
431          */
432         remove : true,
433         /**
434          * @event update
435          * Fires when a Record has been updated
436          * @param {Store} this
437          * @param {Roo.data.Record} record The Record that was updated
438          * @param {String} operation The update operation being performed.  Value may be one of:
439          * <pre><code>
440  Roo.data.Record.EDIT
441  Roo.data.Record.REJECT
442  Roo.data.Record.COMMIT
443          * </code></pre>
444          */
445         update : true,
446         /**
447          * @event clear
448          * Fires when the data cache has been cleared.
449          * @param {Store} this
450          */
451         clear : true,
452         /**
453          * @event beforeload
454          * Fires before a request is made for a new data object.  If the beforeload handler returns false
455          * the load action will be canceled.
456          * @param {Store} this
457          * @param {Object} options The loading options that were specified (see {@link #load} for details)
458          */
459         beforeload : true,
460         /**
461          * @event beforeloadadd
462          * Fires after a new set of Records has been loaded.
463          * @param {Store} this
464          * @param {Roo.data.Record[]} records The Records that were loaded
465          * @param {Object} options The loading options that were specified (see {@link #load} for details)
466          */
467         beforeloadadd : true,
468         /**
469          * @event load
470          * Fires after a new set of Records has been loaded, before they are added to the store.
471          * @param {Store} this
472          * @param {Roo.data.Record[]} records The Records that were loaded
473          * @param {Object} options The loading options that were specified (see {@link #load} for details)
474          * @params {Object} return from reader
475          */
476         load : true,
477         /**
478          * @event loadexception
479          * Fires if an exception occurs in the Proxy during loading.
480          * Called with the signature of the Proxy's "loadexception" event.
481          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
482          * 
483          * @param {Proxy} 
484          * @param {Object} return from JsonData.reader() - success, totalRecords, records
485          * @param {Object} load options 
486          * @param {Object} jsonData from your request (normally this contains the Exception)
487          */
488         loadexception : true
489     });
490     
491     if(this.proxy){
492         this.proxy = Roo.factory(this.proxy, Roo.data);
493         this.proxy.xmodule = this.xmodule || false;
494         this.relayEvents(this.proxy,  ["loadexception"]);
495     }
496     this.sortToggle = {};
497     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
498
499     Roo.data.Store.superclass.constructor.call(this);
500
501     if(this.inlineData){
502         this.loadData(this.inlineData);
503         delete this.inlineData;
504     }
505 };
506
507 Roo.extend(Roo.data.Store, Roo.util.Observable, {
508      /**
509     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
510     * without a remote query - used by combo/forms at present.
511     */
512     
513     /**
514     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
515     */
516     /**
517     * @cfg {Array} data Inline data to be loaded when the store is initialized.
518     */
519     /**
520     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
521     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
522     */
523     /**
524     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
525     * on any HTTP request
526     */
527     /**
528     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
529     */
530     /**
531     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
532     */
533     multiSort: false,
534     /**
535     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
536     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
537     */
538     remoteSort : false,
539
540     /**
541     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
542      * loaded or when a record is removed. (defaults to false).
543     */
544     pruneModifiedRecords : false,
545
546     // private
547     lastOptions : null,
548
549     /**
550      * Add Records to the Store and fires the add event.
551      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
552      */
553     add : function(records){
554         records = [].concat(records);
555         for(var i = 0, len = records.length; i < len; i++){
556             records[i].join(this);
557         }
558         var index = this.data.length;
559         this.data.addAll(records);
560         this.fireEvent("add", this, records, index);
561     },
562
563     /**
564      * Remove a Record from the Store and fires the remove event.
565      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
566      */
567     remove : function(record){
568         var index = this.data.indexOf(record);
569         this.data.removeAt(index);
570  
571         if(this.pruneModifiedRecords){
572             this.modified.remove(record);
573         }
574         this.fireEvent("remove", this, record, index);
575     },
576
577     /**
578      * Remove all Records from the Store and fires the clear event.
579      */
580     removeAll : function(){
581         this.data.clear();
582         if(this.pruneModifiedRecords){
583             this.modified = [];
584         }
585         this.fireEvent("clear", this);
586     },
587
588     /**
589      * Inserts Records to the Store at the given index and fires the add event.
590      * @param {Number} index The start index at which to insert the passed Records.
591      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
592      */
593     insert : function(index, records){
594         records = [].concat(records);
595         for(var i = 0, len = records.length; i < len; i++){
596             this.data.insert(index, records[i]);
597             records[i].join(this);
598         }
599         this.fireEvent("add", this, records, index);
600     },
601
602     /**
603      * Get the index within the cache of the passed Record.
604      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
605      * @return {Number} The index of the passed Record. Returns -1 if not found.
606      */
607     indexOf : function(record){
608         return this.data.indexOf(record);
609     },
610
611     /**
612      * Get the index within the cache of the Record with the passed id.
613      * @param {String} id The id of the Record to find.
614      * @return {Number} The index of the Record. Returns -1 if not found.
615      */
616     indexOfId : function(id){
617         return this.data.indexOfKey(id);
618     },
619
620     /**
621      * Get the Record with the specified id.
622      * @param {String} id The id of the Record to find.
623      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
624      */
625     getById : function(id){
626         return this.data.key(id);
627     },
628
629     /**
630      * Get the Record at the specified index.
631      * @param {Number} index The index of the Record to find.
632      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
633      */
634     getAt : function(index){
635         return this.data.itemAt(index);
636     },
637
638     /**
639      * Returns a range of Records between specified indices.
640      * @param {Number} startIndex (optional) The starting index (defaults to 0)
641      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
642      * @return {Roo.data.Record[]} An array of Records
643      */
644     getRange : function(start, end){
645         return this.data.getRange(start, end);
646     },
647
648     // private
649     storeOptions : function(o){
650         o = Roo.apply({}, o);
651         delete o.callback;
652         delete o.scope;
653         this.lastOptions = o;
654     },
655
656     /**
657      * Loads the Record cache from the configured Proxy using the configured Reader.
658      * <p>
659      * If using remote paging, then the first load call must specify the <em>start</em>
660      * and <em>limit</em> properties in the options.params property to establish the initial
661      * position within the dataset, and the number of Records to cache on each read from the Proxy.
662      * <p>
663      * <strong>It is important to note that for remote data sources, loading is asynchronous,
664      * and this call will return before the new data has been loaded. Perform any post-processing
665      * in a callback function, or in a "load" event handler.</strong>
666      * <p>
667      * @param {Object} options An object containing properties which control loading options:<ul>
668      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
669      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
670      * passed the following arguments:<ul>
671      * <li>r : Roo.data.Record[]</li>
672      * <li>options: Options object from the load call</li>
673      * <li>success: Boolean success indicator</li></ul></li>
674      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
675      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
676      * </ul>
677      */
678     load : function(options){
679         options = options || {};
680         if(this.fireEvent("beforeload", this, options) !== false){
681             this.storeOptions(options);
682             var p = Roo.apply(options.params || {}, this.baseParams);
683             // if meta was not loaded from remote source.. try requesting it.
684             if (!this.reader.metaFromRemote) {
685                 p._requestMeta = 1;
686             }
687             if(this.sortInfo && this.remoteSort){
688                 var pn = this.paramNames;
689                 p[pn["sort"]] = this.sortInfo.field;
690                 p[pn["dir"]] = this.sortInfo.direction;
691             }
692             if (this.multiSort) {
693                 var pn = this.paramNames;
694                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
695             }
696             
697             this.proxy.load(p, this.reader, this.loadRecords, this, options);
698         }
699     },
700
701     /**
702      * Reloads the Record cache from the configured Proxy using the configured Reader and
703      * the options from the last load operation performed.
704      * @param {Object} options (optional) An object containing properties which may override the options
705      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
706      * the most recently used options are reused).
707      */
708     reload : function(options){
709         this.load(Roo.applyIf(options||{}, this.lastOptions));
710     },
711
712     // private
713     // Called as a callback by the Reader during a load operation.
714     loadRecords : function(o, options, success){
715         if(!o || success === false){
716             if(success !== false){
717                 this.fireEvent("load", this, [], options, o);
718             }
719             if(options.callback){
720                 options.callback.call(options.scope || this, [], options, false);
721             }
722             return;
723         }
724         // if data returned failure - throw an exception.
725         if (o.success === false) {
726             // show a message if no listener is registered.
727             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
728                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
729             }
730             // loadmask wil be hooked into this..
731             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
732             return;
733         }
734         var r = o.records, t = o.totalRecords || r.length;
735         
736         this.fireEvent("beforeloadadd", this, r, options, o);
737         
738         if(!options || options.add !== true){
739             if(this.pruneModifiedRecords){
740                 this.modified = [];
741             }
742             for(var i = 0, len = r.length; i < len; i++){
743                 r[i].join(this);
744             }
745             if(this.snapshot){
746                 this.data = this.snapshot;
747                 delete this.snapshot;
748             }
749             this.data.clear();
750             this.data.addAll(r);
751             this.totalLength = t;
752             this.applySort();
753             this.fireEvent("datachanged", this);
754         }else{
755             this.totalLength = Math.max(t, this.data.length+r.length);
756             this.add(r);
757         }
758         
759         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
760                 
761             var e = new Roo.data.Record({});
762
763             e.set(this.parent.displayField, this.parent.emptyTitle);
764             e.set(this.parent.valueField, '');
765
766             this.insert(0, e);
767         }
768             
769         this.fireEvent("load", this, r, options, o);
770         if(options.callback){
771             options.callback.call(options.scope || this, r, options, true);
772         }
773     },
774
775
776     /**
777      * Loads data from a passed data block. A Reader which understands the format of the data
778      * must have been configured in the constructor.
779      * @param {Object} data The data block from which to read the Records.  The format of the data expected
780      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
781      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
782      */
783     loadData : function(o, append){
784         var r = this.reader.readRecords(o);
785         this.loadRecords(r, {add: append}, true);
786     },
787     
788      /**
789      * using 'cn' the nested child reader read the child array into it's child stores.
790      * @param {Object} rec The record with a 'children array
791      */
792     loadDataFromChildren : function(rec)
793     {
794         this.loadData(this.reader.toLoadData(rec));
795     },
796     
797
798     /**
799      * Gets the number of cached records.
800      * <p>
801      * <em>If using paging, this may not be the total size of the dataset. If the data object
802      * used by the Reader contains the dataset size, then the getTotalCount() function returns
803      * the data set size</em>
804      */
805     getCount : function(){
806         return this.data.length || 0;
807     },
808
809     /**
810      * Gets the total number of records in the dataset as returned by the server.
811      * <p>
812      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
813      * the dataset size</em>
814      */
815     getTotalCount : function(){
816         return this.totalLength || 0;
817     },
818
819     /**
820      * Returns the sort state of the Store as an object with two properties:
821      * <pre><code>
822  field {String} The name of the field by which the Records are sorted
823  direction {String} The sort order, "ASC" or "DESC"
824      * </code></pre>
825      */
826     getSortState : function(){
827         return this.sortInfo;
828     },
829
830     // private
831     applySort : function(){
832         if(this.sortInfo && !this.remoteSort){
833             var s = this.sortInfo, f = s.field;
834             var st = this.fields.get(f).sortType;
835             var fn = function(r1, r2){
836                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
837                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
838             };
839             this.data.sort(s.direction, fn);
840             if(this.snapshot && this.snapshot != this.data){
841                 this.snapshot.sort(s.direction, fn);
842             }
843         }
844     },
845
846     /**
847      * Sets the default sort column and order to be used by the next load operation.
848      * @param {String} fieldName The name of the field to sort by.
849      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
850      */
851     setDefaultSort : function(field, dir){
852         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
853     },
854
855     /**
856      * Sort the Records.
857      * If remote sorting is used, the sort is performed on the server, and the cache is
858      * reloaded. If local sorting is used, the cache is sorted internally.
859      * @param {String} fieldName The name of the field to sort by.
860      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
861      */
862     sort : function(fieldName, dir){
863         var f = this.fields.get(fieldName);
864         if(!dir){
865             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
866             
867             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
868                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
869             }else{
870                 dir = f.sortDir;
871             }
872         }
873         this.sortToggle[f.name] = dir;
874         this.sortInfo = {field: f.name, direction: dir};
875         if(!this.remoteSort){
876             this.applySort();
877             this.fireEvent("datachanged", this);
878         }else{
879             this.load(this.lastOptions);
880         }
881     },
882
883     /**
884      * Calls the specified function for each of the Records in the cache.
885      * @param {Function} fn The function to call. The Record is passed as the first parameter.
886      * Returning <em>false</em> aborts and exits the iteration.
887      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
888      */
889     each : function(fn, scope){
890         this.data.each(fn, scope);
891     },
892
893     /**
894      * Gets all records modified since the last commit.  Modified records are persisted across load operations
895      * (e.g., during paging).
896      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
897      */
898     getModifiedRecords : function(){
899         return this.modified;
900     },
901
902     // private
903     createFilterFn : function(property, value, anyMatch){
904         if(!value.exec){ // not a regex
905             value = String(value);
906             if(value.length == 0){
907                 return false;
908             }
909             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
910         }
911         return function(r){
912             return value.test(r.data[property]);
913         };
914     },
915
916     /**
917      * Sums the value of <i>property</i> for each record between start and end and returns the result.
918      * @param {String} property A field on your records
919      * @param {Number} start The record index to start at (defaults to 0)
920      * @param {Number} end The last record index to include (defaults to length - 1)
921      * @return {Number} The sum
922      */
923     sum : function(property, start, end){
924         var rs = this.data.items, v = 0;
925         start = start || 0;
926         end = (end || end === 0) ? end : rs.length-1;
927
928         for(var i = start; i <= end; i++){
929             v += (rs[i].data[property] || 0);
930         }
931         return v;
932     },
933
934     /**
935      * Filter the records by a specified property.
936      * @param {String} field A field on your records
937      * @param {String/RegExp} value Either a string that the field
938      * should start with or a RegExp to test against the field
939      * @param {Boolean} anyMatch True to match any part not just the beginning
940      */
941     filter : function(property, value, anyMatch){
942         var fn = this.createFilterFn(property, value, anyMatch);
943         return fn ? this.filterBy(fn) : this.clearFilter();
944     },
945
946     /**
947      * Filter by a function. The specified function will be called with each
948      * record in this data source. If the function returns true the record is included,
949      * otherwise it is filtered.
950      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
951      * @param {Object} scope (optional) The scope of the function (defaults to this)
952      */
953     filterBy : function(fn, scope){
954         this.snapshot = this.snapshot || this.data;
955         this.data = this.queryBy(fn, scope||this);
956         this.fireEvent("datachanged", this);
957     },
958
959     /**
960      * Query the records by a specified property.
961      * @param {String} field A field on your records
962      * @param {String/RegExp} value Either a string that the field
963      * should start with or a RegExp to test against the field
964      * @param {Boolean} anyMatch True to match any part not just the beginning
965      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
966      */
967     query : function(property, value, anyMatch){
968         var fn = this.createFilterFn(property, value, anyMatch);
969         return fn ? this.queryBy(fn) : this.data.clone();
970     },
971
972     /**
973      * Query by a function. The specified function will be called with each
974      * record in this data source. If the function returns true the record is included
975      * in the results.
976      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
977      * @param {Object} scope (optional) The scope of the function (defaults to this)
978       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
979      **/
980     queryBy : function(fn, scope){
981         var data = this.snapshot || this.data;
982         return data.filterBy(fn, scope||this);
983     },
984
985     /**
986      * Collects unique values for a particular dataIndex from this store.
987      * @param {String} dataIndex The property to collect
988      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
989      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
990      * @return {Array} An array of the unique values
991      **/
992     collect : function(dataIndex, allowNull, bypassFilter){
993         var d = (bypassFilter === true && this.snapshot) ?
994                 this.snapshot.items : this.data.items;
995         var v, sv, r = [], l = {};
996         for(var i = 0, len = d.length; i < len; i++){
997             v = d[i].data[dataIndex];
998             sv = String(v);
999             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
1000                 l[sv] = true;
1001                 r[r.length] = v;
1002             }
1003         }
1004         return r;
1005     },
1006
1007     /**
1008      * Revert to a view of the Record cache with no filtering applied.
1009      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
1010      */
1011     clearFilter : function(suppressEvent){
1012         if(this.snapshot && this.snapshot != this.data){
1013             this.data = this.snapshot;
1014             delete this.snapshot;
1015             if(suppressEvent !== true){
1016                 this.fireEvent("datachanged", this);
1017             }
1018         }
1019     },
1020
1021     // private
1022     afterEdit : function(record){
1023         if(this.modified.indexOf(record) == -1){
1024             this.modified.push(record);
1025         }
1026         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1027     },
1028     
1029     // private
1030     afterReject : function(record){
1031         this.modified.remove(record);
1032         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1033     },
1034
1035     // private
1036     afterCommit : function(record){
1037         this.modified.remove(record);
1038         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1039     },
1040
1041     /**
1042      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1043      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1044      */
1045     commitChanges : function(){
1046         var m = this.modified.slice(0);
1047         this.modified = [];
1048         for(var i = 0, len = m.length; i < len; i++){
1049             m[i].commit();
1050         }
1051     },
1052
1053     /**
1054      * Cancel outstanding changes on all changed records.
1055      */
1056     rejectChanges : function(){
1057         var m = this.modified.slice(0);
1058         this.modified = [];
1059         for(var i = 0, len = m.length; i < len; i++){
1060             m[i].reject();
1061         }
1062     },
1063
1064     onMetaChange : function(meta, rtype, o){
1065         this.recordType = rtype;
1066         this.fields = rtype.prototype.fields;
1067         delete this.snapshot;
1068         this.sortInfo = meta.sortInfo || this.sortInfo;
1069         this.modified = [];
1070         this.fireEvent('metachange', this, this.reader.meta);
1071     },
1072     
1073     moveIndex : function(data, type)
1074     {
1075         var index = this.indexOf(data);
1076         
1077         var newIndex = index + type;
1078         
1079         this.remove(data);
1080         
1081         this.insert(newIndex, data);
1082         
1083     }
1084 });/*
1085  * Based on:
1086  * Ext JS Library 1.1.1
1087  * Copyright(c) 2006-2007, Ext JS, LLC.
1088  *
1089  * Originally Released Under LGPL - original licence link has changed is not relivant.
1090  *
1091  * Fork - LGPL
1092  * <script type="text/javascript">
1093  */
1094
1095 /**
1096  * @class Roo.data.SimpleStore
1097  * @extends Roo.data.Store
1098  * Small helper class to make creating Stores from Array data easier.
1099  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1100  * @cfg {Array} fields An array of field definition objects, or field name strings.
1101  * @cfg {Object} an existing reader (eg. copied from another store)
1102  * @cfg {Array} data The multi-dimensional array of data
1103  * @cfg {Roo.data.DataProxy} proxy [not-required]  
1104  * @cfg {Roo.data.Reader} reader  [not-required] 
1105  * @constructor
1106  * @param {Object} config
1107  */
1108 Roo.data.SimpleStore = function(config)
1109 {
1110     Roo.data.SimpleStore.superclass.constructor.call(this, {
1111         isLocal : true,
1112         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
1113                 id: config.id
1114             },
1115             Roo.data.Record.create(config.fields)
1116         ),
1117         proxy : new Roo.data.MemoryProxy(config.data)
1118     });
1119     this.load();
1120 };
1121 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1122  * Based on:
1123  * Ext JS Library 1.1.1
1124  * Copyright(c) 2006-2007, Ext JS, LLC.
1125  *
1126  * Originally Released Under LGPL - original licence link has changed is not relivant.
1127  *
1128  * Fork - LGPL
1129  * <script type="text/javascript">
1130  */
1131
1132 /**
1133 /**
1134  * @extends Roo.data.Store
1135  * @class Roo.data.JsonStore
1136  * Small helper class to make creating Stores for JSON data easier. <br/>
1137 <pre><code>
1138 var store = new Roo.data.JsonStore({
1139     url: 'get-images.php',
1140     root: 'images',
1141     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1142 });
1143 </code></pre>
1144  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1145  * JsonReader and HttpProxy (unless inline data is provided).</b>
1146  * @cfg {Array} fields An array of field definition objects, or field name strings.
1147  * @constructor
1148  * @param {Object} config
1149  */
1150 Roo.data.JsonStore = function(c){
1151     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1152         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1153         reader: new Roo.data.JsonReader(c, c.fields)
1154     }));
1155 };
1156 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1157  * Based on:
1158  * Ext JS Library 1.1.1
1159  * Copyright(c) 2006-2007, Ext JS, LLC.
1160  *
1161  * Originally Released Under LGPL - original licence link has changed is not relivant.
1162  *
1163  * Fork - LGPL
1164  * <script type="text/javascript">
1165  */
1166
1167  
1168 Roo.data.Field = function(config){
1169     if(typeof config == "string"){
1170         config = {name: config};
1171     }
1172     Roo.apply(this, config);
1173     
1174     if(!this.type){
1175         this.type = "auto";
1176     }
1177     
1178     var st = Roo.data.SortTypes;
1179     // named sortTypes are supported, here we look them up
1180     if(typeof this.sortType == "string"){
1181         this.sortType = st[this.sortType];
1182     }
1183     
1184     // set default sortType for strings and dates
1185     if(!this.sortType){
1186         switch(this.type){
1187             case "string":
1188                 this.sortType = st.asUCString;
1189                 break;
1190             case "date":
1191                 this.sortType = st.asDate;
1192                 break;
1193             default:
1194                 this.sortType = st.none;
1195         }
1196     }
1197
1198     // define once
1199     var stripRe = /[\$,%]/g;
1200
1201     // prebuilt conversion function for this field, instead of
1202     // switching every time we're reading a value
1203     if(!this.convert){
1204         var cv, dateFormat = this.dateFormat;
1205         switch(this.type){
1206             case "":
1207             case "auto":
1208             case undefined:
1209                 cv = function(v){ return v; };
1210                 break;
1211             case "string":
1212                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1213                 break;
1214             case "int":
1215                 cv = function(v){
1216                     return v !== undefined && v !== null && v !== '' ?
1217                            parseInt(String(v).replace(stripRe, ""), 10) : '';
1218                     };
1219                 break;
1220             case "float":
1221                 cv = function(v){
1222                     return v !== undefined && v !== null && v !== '' ?
1223                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
1224                     };
1225                 break;
1226             case "bool":
1227             case "boolean":
1228                 cv = function(v){ return v === true || v === "true" || v == 1; };
1229                 break;
1230             case "date":
1231                 cv = function(v){
1232                     if(!v){
1233                         return '';
1234                     }
1235                     if(v instanceof Date){
1236                         return v;
1237                     }
1238                     if(dateFormat){
1239                         if(dateFormat == "timestamp"){
1240                             return new Date(v*1000);
1241                         }
1242                         return Date.parseDate(v, dateFormat);
1243                     }
1244                     var parsed = Date.parse(v);
1245                     return parsed ? new Date(parsed) : null;
1246                 };
1247              break;
1248             
1249         }
1250         this.convert = cv;
1251     }
1252 };
1253
1254 Roo.data.Field.prototype = {
1255     dateFormat: null,
1256     defaultValue: "",
1257     mapping: null,
1258     sortType : null,
1259     sortDir : "ASC"
1260 };/*
1261  * Based on:
1262  * Ext JS Library 1.1.1
1263  * Copyright(c) 2006-2007, Ext JS, LLC.
1264  *
1265  * Originally Released Under LGPL - original licence link has changed is not relivant.
1266  *
1267  * Fork - LGPL
1268  * <script type="text/javascript">
1269  */
1270  
1271 // Base class for reading structured data from a data source.  This class is intended to be
1272 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1273
1274 /**
1275  * @class Roo.data.DataReader
1276  * @abstract
1277  * Base class for reading structured data from a data source.  This class is intended to be
1278  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1279  */
1280
1281 Roo.data.DataReader = function(meta, recordType){
1282     
1283     this.meta = meta;
1284     
1285     this.recordType = recordType instanceof Array ? 
1286         Roo.data.Record.create(recordType) : recordType;
1287 };
1288
1289 Roo.data.DataReader.prototype = {
1290     
1291     
1292     readerType : 'Data',
1293      /**
1294      * Create an empty record
1295      * @param {Object} data (optional) - overlay some values
1296      * @return {Roo.data.Record} record created.
1297      */
1298     newRow :  function(d) {
1299         var da =  {};
1300         this.recordType.prototype.fields.each(function(c) {
1301             switch( c.type) {
1302                 case 'int' : da[c.name] = 0; break;
1303                 case 'date' : da[c.name] = new Date(); break;
1304                 case 'float' : da[c.name] = 0.0; break;
1305                 case 'boolean' : da[c.name] = false; break;
1306                 default : da[c.name] = ""; break;
1307             }
1308             
1309         });
1310         return new this.recordType(Roo.apply(da, d));
1311     }
1312     
1313     
1314 };/*
1315  * Based on:
1316  * Ext JS Library 1.1.1
1317  * Copyright(c) 2006-2007, Ext JS, LLC.
1318  *
1319  * Originally Released Under LGPL - original licence link has changed is not relivant.
1320  *
1321  * Fork - LGPL
1322  * <script type="text/javascript">
1323  */
1324
1325 /**
1326  * @class Roo.data.DataProxy
1327  * @extends Roo.util.Observable
1328  * @abstract
1329  * This class is an abstract base class for implementations which provide retrieval of
1330  * unformatted data objects.<br>
1331  * <p>
1332  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1333  * (of the appropriate type which knows how to parse the data object) to provide a block of
1334  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1335  * <p>
1336  * Custom implementations must implement the load method as described in
1337  * {@link Roo.data.HttpProxy#load}.
1338  */
1339 Roo.data.DataProxy = function(){
1340     this.addEvents({
1341         /**
1342          * @event beforeload
1343          * Fires before a network request is made to retrieve a data object.
1344          * @param {Object} This DataProxy object.
1345          * @param {Object} params The params parameter to the load function.
1346          */
1347         beforeload : true,
1348         /**
1349          * @event load
1350          * Fires before the load method's callback is called.
1351          * @param {Object} This DataProxy object.
1352          * @param {Object} o The data object.
1353          * @param {Object} arg The callback argument object passed to the load function.
1354          */
1355         load : true,
1356         /**
1357          * @event loadexception
1358          * Fires if an Exception occurs during data retrieval.
1359          * @param {Object} This DataProxy object.
1360          * @param {Object} o The data object.
1361          * @param {Object} arg The callback argument object passed to the load function.
1362          * @param {Object} e The Exception.
1363          */
1364         loadexception : true
1365     });
1366     Roo.data.DataProxy.superclass.constructor.call(this);
1367 };
1368
1369 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1370
1371     /**
1372      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1373      */
1374 /*
1375  * Based on:
1376  * Ext JS Library 1.1.1
1377  * Copyright(c) 2006-2007, Ext JS, LLC.
1378  *
1379  * Originally Released Under LGPL - original licence link has changed is not relivant.
1380  *
1381  * Fork - LGPL
1382  * <script type="text/javascript">
1383  */
1384 /**
1385  * @class Roo.data.MemoryProxy
1386  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1387  * to the Reader when its load method is called.
1388  * @constructor
1389  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1390  */
1391 Roo.data.MemoryProxy = function(data){
1392     if (data.data) {
1393         data = data.data;
1394     }
1395     Roo.data.MemoryProxy.superclass.constructor.call(this);
1396     this.data = data;
1397 };
1398
1399 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1400     
1401     /**
1402      * Load data from the requested source (in this case an in-memory
1403      * data object passed to the constructor), read the data object into
1404      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1405      * process that block using the passed callback.
1406      * @param {Object} params This parameter is not used by the MemoryProxy class.
1407      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1408      * object into a block of Roo.data.Records.
1409      * @param {Function} callback The function into which to pass the block of Roo.data.records.
1410      * The function must be passed <ul>
1411      * <li>The Record block object</li>
1412      * <li>The "arg" argument from the load function</li>
1413      * <li>A boolean success indicator</li>
1414      * </ul>
1415      * @param {Object} scope The scope in which to call the callback
1416      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1417      */
1418     load : function(params, reader, callback, scope, arg){
1419         params = params || {};
1420         var result;
1421         try {
1422             result = reader.readRecords(params.data ? params.data :this.data);
1423         }catch(e){
1424             this.fireEvent("loadexception", this, arg, null, e);
1425             callback.call(scope, null, arg, false);
1426             return;
1427         }
1428         callback.call(scope, result, arg, true);
1429     },
1430     
1431     // private
1432     update : function(params, records){
1433         
1434     }
1435 });/*
1436  * Based on:
1437  * Ext JS Library 1.1.1
1438  * Copyright(c) 2006-2007, Ext JS, LLC.
1439  *
1440  * Originally Released Under LGPL - original licence link has changed is not relivant.
1441  *
1442  * Fork - LGPL
1443  * <script type="text/javascript">
1444  */
1445 /**
1446  * @class Roo.data.HttpProxy
1447  * @extends Roo.data.DataProxy
1448  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1449  * configured to reference a certain URL.<br><br>
1450  * <p>
1451  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1452  * from which the running page was served.<br><br>
1453  * <p>
1454  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1455  * <p>
1456  * Be aware that to enable the browser to parse an XML document, the server must set
1457  * the Content-Type header in the HTTP response to "text/xml".
1458  * @constructor
1459  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1460  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
1461  * will be used to make the request.
1462  */
1463 Roo.data.HttpProxy = function(conn){
1464     Roo.data.HttpProxy.superclass.constructor.call(this);
1465     // is conn a conn config or a real conn?
1466     this.conn = conn;
1467     this.useAjax = !conn || !conn.events;
1468   
1469 };
1470
1471 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1472     // thse are take from connection...
1473     
1474     /**
1475      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1476      */
1477     /**
1478      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1479      * extra parameters to each request made by this object. (defaults to undefined)
1480      */
1481     /**
1482      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1483      *  to each request made by this object. (defaults to undefined)
1484      */
1485     /**
1486      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
1487      */
1488     /**
1489      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1490      */
1491      /**
1492      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1493      * @type Boolean
1494      */
1495   
1496
1497     /**
1498      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1499      * @type Boolean
1500      */
1501     /**
1502      * Return the {@link Roo.data.Connection} object being used by this Proxy.
1503      * @return {Connection} The Connection object. This object may be used to subscribe to events on
1504      * a finer-grained basis than the DataProxy events.
1505      */
1506     getConnection : function(){
1507         return this.useAjax ? Roo.Ajax : this.conn;
1508     },
1509
1510     /**
1511      * Load data from the configured {@link Roo.data.Connection}, read the data object into
1512      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1513      * process that block using the passed callback.
1514      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1515      * for the request to the remote server.
1516      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1517      * object into a block of Roo.data.Records.
1518      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1519      * The function must be passed <ul>
1520      * <li>The Record block object</li>
1521      * <li>The "arg" argument from the load function</li>
1522      * <li>A boolean success indicator</li>
1523      * </ul>
1524      * @param {Object} scope The scope in which to call the callback
1525      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1526      */
1527     load : function(params, reader, callback, scope, arg){
1528         if(this.fireEvent("beforeload", this, params) !== false){
1529             var  o = {
1530                 params : params || {},
1531                 request: {
1532                     callback : callback,
1533                     scope : scope,
1534                     arg : arg
1535                 },
1536                 reader: reader,
1537                 callback : this.loadResponse,
1538                 scope: this
1539             };
1540             if(this.useAjax){
1541                 Roo.applyIf(o, this.conn);
1542                 if(this.activeRequest){
1543                     Roo.Ajax.abort(this.activeRequest);
1544                 }
1545                 this.activeRequest = Roo.Ajax.request(o);
1546             }else{
1547                 this.conn.request(o);
1548             }
1549         }else{
1550             callback.call(scope||this, null, arg, false);
1551         }
1552     },
1553
1554     // private
1555     loadResponse : function(o, success, response){
1556         delete this.activeRequest;
1557         if(!success){
1558             this.fireEvent("loadexception", this, o, response);
1559             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1560             return;
1561         }
1562         var result;
1563         try {
1564             result = o.reader.read(response);
1565         }catch(e){
1566             this.fireEvent("loadexception", this, o, response, e);
1567             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1568             return;
1569         }
1570         
1571         this.fireEvent("load", this, o, o.request.arg);
1572         o.request.callback.call(o.request.scope, result, o.request.arg, true);
1573     },
1574
1575     // private
1576     update : function(dataSet){
1577
1578     },
1579
1580     // private
1581     updateResponse : function(dataSet){
1582
1583     }
1584 });/*
1585  * Based on:
1586  * Ext JS Library 1.1.1
1587  * Copyright(c) 2006-2007, Ext JS, LLC.
1588  *
1589  * Originally Released Under LGPL - original licence link has changed is not relivant.
1590  *
1591  * Fork - LGPL
1592  * <script type="text/javascript">
1593  */
1594
1595 /**
1596  * @class Roo.data.ScriptTagProxy
1597  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1598  * other than the originating domain of the running page.<br><br>
1599  * <p>
1600  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
1601  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1602  * <p>
1603  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1604  * source code that is used as the source inside a &lt;script> tag.<br><br>
1605  * <p>
1606  * In order for the browser to process the returned data, the server must wrap the data object
1607  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1608  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1609  * depending on whether the callback name was passed:
1610  * <p>
1611  * <pre><code>
1612 boolean scriptTag = false;
1613 String cb = request.getParameter("callback");
1614 if (cb != null) {
1615     scriptTag = true;
1616     response.setContentType("text/javascript");
1617 } else {
1618     response.setContentType("application/x-json");
1619 }
1620 Writer out = response.getWriter();
1621 if (scriptTag) {
1622     out.write(cb + "(");
1623 }
1624 out.print(dataBlock.toJsonString());
1625 if (scriptTag) {
1626     out.write(");");
1627 }
1628 </pre></code>
1629  *
1630  * @constructor
1631  * @param {Object} config A configuration object.
1632  */
1633 Roo.data.ScriptTagProxy = function(config){
1634     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1635     Roo.apply(this, config);
1636     this.head = document.getElementsByTagName("head")[0];
1637 };
1638
1639 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1640
1641 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1642     /**
1643      * @cfg {String} url The URL from which to request the data object.
1644      */
1645     /**
1646      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1647      */
1648     timeout : 30000,
1649     /**
1650      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1651      * the server the name of the callback function set up by the load call to process the returned data object.
1652      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1653      * javascript output which calls this named function passing the data object as its only parameter.
1654      */
1655     callbackParam : "callback",
1656     /**
1657      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1658      * name to the request.
1659      */
1660     nocache : true,
1661
1662     /**
1663      * Load data from the configured URL, read the data object into
1664      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1665      * process that block using the passed callback.
1666      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1667      * for the request to the remote server.
1668      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1669      * object into a block of Roo.data.Records.
1670      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1671      * The function must be passed <ul>
1672      * <li>The Record block object</li>
1673      * <li>The "arg" argument from the load function</li>
1674      * <li>A boolean success indicator</li>
1675      * </ul>
1676      * @param {Object} scope The scope in which to call the callback
1677      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1678      */
1679     load : function(params, reader, callback, scope, arg){
1680         if(this.fireEvent("beforeload", this, params) !== false){
1681
1682             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1683
1684             var url = this.url;
1685             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1686             if(this.nocache){
1687                 url += "&_dc=" + (new Date().getTime());
1688             }
1689             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1690             var trans = {
1691                 id : transId,
1692                 cb : "stcCallback"+transId,
1693                 scriptId : "stcScript"+transId,
1694                 params : params,
1695                 arg : arg,
1696                 url : url,
1697                 callback : callback,
1698                 scope : scope,
1699                 reader : reader
1700             };
1701             var conn = this;
1702
1703             window[trans.cb] = function(o){
1704                 conn.handleResponse(o, trans);
1705             };
1706
1707             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1708
1709             if(this.autoAbort !== false){
1710                 this.abort();
1711             }
1712
1713             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1714
1715             var script = document.createElement("script");
1716             script.setAttribute("src", url);
1717             script.setAttribute("type", "text/javascript");
1718             script.setAttribute("id", trans.scriptId);
1719             this.head.appendChild(script);
1720
1721             this.trans = trans;
1722         }else{
1723             callback.call(scope||this, null, arg, false);
1724         }
1725     },
1726
1727     // private
1728     isLoading : function(){
1729         return this.trans ? true : false;
1730     },
1731
1732     /**
1733      * Abort the current server request.
1734      */
1735     abort : function(){
1736         if(this.isLoading()){
1737             this.destroyTrans(this.trans);
1738         }
1739     },
1740
1741     // private
1742     destroyTrans : function(trans, isLoaded){
1743         this.head.removeChild(document.getElementById(trans.scriptId));
1744         clearTimeout(trans.timeoutId);
1745         if(isLoaded){
1746             window[trans.cb] = undefined;
1747             try{
1748                 delete window[trans.cb];
1749             }catch(e){}
1750         }else{
1751             // if hasn't been loaded, wait for load to remove it to prevent script error
1752             window[trans.cb] = function(){
1753                 window[trans.cb] = undefined;
1754                 try{
1755                     delete window[trans.cb];
1756                 }catch(e){}
1757             };
1758         }
1759     },
1760
1761     // private
1762     handleResponse : function(o, trans){
1763         this.trans = false;
1764         this.destroyTrans(trans, true);
1765         var result;
1766         try {
1767             result = trans.reader.readRecords(o);
1768         }catch(e){
1769             this.fireEvent("loadexception", this, o, trans.arg, e);
1770             trans.callback.call(trans.scope||window, null, trans.arg, false);
1771             return;
1772         }
1773         this.fireEvent("load", this, o, trans.arg);
1774         trans.callback.call(trans.scope||window, result, trans.arg, true);
1775     },
1776
1777     // private
1778     handleFailure : function(trans){
1779         this.trans = false;
1780         this.destroyTrans(trans, false);
1781         this.fireEvent("loadexception", this, null, trans.arg);
1782         trans.callback.call(trans.scope||window, null, trans.arg, false);
1783     }
1784 });/*
1785  * Based on:
1786  * Ext JS Library 1.1.1
1787  * Copyright(c) 2006-2007, Ext JS, LLC.
1788  *
1789  * Originally Released Under LGPL - original licence link has changed is not relivant.
1790  *
1791  * Fork - LGPL
1792  * <script type="text/javascript">
1793  */
1794
1795 /**
1796  * @class Roo.data.JsonReader
1797  * @extends Roo.data.DataReader
1798  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1799  * based on mappings in a provided Roo.data.Record constructor.
1800  * 
1801  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1802  * in the reply previously. 
1803  * 
1804  * <p>
1805  * Example code:
1806  * <pre><code>
1807 var RecordDef = Roo.data.Record.create([
1808     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1809     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
1810 ]);
1811 var myReader = new Roo.data.JsonReader({
1812     totalProperty: "results",    // The property which contains the total dataset size (optional)
1813     root: "rows",                // The property which contains an Array of row objects
1814     id: "id"                     // The property within each row object that provides an ID for the record (optional)
1815 }, RecordDef);
1816 </code></pre>
1817  * <p>
1818  * This would consume a JSON file like this:
1819  * <pre><code>
1820 { 'results': 2, 'rows': [
1821     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1822     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1823 }
1824 </code></pre>
1825  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1826  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1827  * paged from the remote server.
1828  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1829  * @cfg {String} root name of the property which contains the Array of row objects.
1830  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1831  * @cfg {Array} fields Array of field definition objects
1832  * @constructor
1833  * Create a new JsonReader
1834  * @param {Object} meta Metadata configuration options
1835  * @param {Object} recordType Either an Array of field definition objects,
1836  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1837  */
1838 Roo.data.JsonReader = function(meta, recordType){
1839     
1840     meta = meta || {};
1841     // set some defaults:
1842     Roo.applyIf(meta, {
1843         totalProperty: 'total',
1844         successProperty : 'success',
1845         root : 'data',
1846         id : 'id'
1847     });
1848     
1849     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1850 };
1851 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1852     
1853     readerType : 'Json',
1854     
1855     /**
1856      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
1857      * Used by Store query builder to append _requestMeta to params.
1858      * 
1859      */
1860     metaFromRemote : false,
1861     /**
1862      * This method is only used by a DataProxy which has retrieved data from a remote server.
1863      * @param {Object} response The XHR object which contains the JSON data in its responseText.
1864      * @return {Object} data A data block which is used by an Roo.data.Store object as
1865      * a cache of Roo.data.Records.
1866      */
1867     read : function(response){
1868         var json = response.responseText;
1869        
1870         var o = /* eval:var:o */ eval("("+json+")");
1871         if(!o) {
1872             throw {message: "JsonReader.read: Json object not found"};
1873         }
1874         
1875         if(o.metaData){
1876             
1877             delete this.ef;
1878             this.metaFromRemote = true;
1879             this.meta = o.metaData;
1880             this.recordType = Roo.data.Record.create(o.metaData.fields);
1881             this.onMetaChange(this.meta, this.recordType, o);
1882         }
1883         return this.readRecords(o);
1884     },
1885
1886     // private function a store will implement
1887     onMetaChange : function(meta, recordType, o){
1888
1889     },
1890
1891     /**
1892          * @ignore
1893          */
1894     simpleAccess: function(obj, subsc) {
1895         return obj[subsc];
1896     },
1897
1898         /**
1899          * @ignore
1900          */
1901     getJsonAccessor: function(){
1902         var re = /[\[\.]/;
1903         return function(expr) {
1904             try {
1905                 return(re.test(expr))
1906                     ? new Function("obj", "return obj." + expr)
1907                     : function(obj){
1908                         return obj[expr];
1909                     };
1910             } catch(e){}
1911             return Roo.emptyFn;
1912         };
1913     }(),
1914
1915     /**
1916      * Create a data block containing Roo.data.Records from an XML document.
1917      * @param {Object} o An object which contains an Array of row objects in the property specified
1918      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1919      * which contains the total size of the dataset.
1920      * @return {Object} data A data block which is used by an Roo.data.Store object as
1921      * a cache of Roo.data.Records.
1922      */
1923     readRecords : function(o){
1924         /**
1925          * After any data loads, the raw JSON data is available for further custom processing.
1926          * @type Object
1927          */
1928         this.o = o;
1929         var s = this.meta, Record = this.recordType,
1930             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1931
1932 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
1933         if (!this.ef) {
1934             if(s.totalProperty) {
1935                     this.getTotal = this.getJsonAccessor(s.totalProperty);
1936                 }
1937                 if(s.successProperty) {
1938                     this.getSuccess = this.getJsonAccessor(s.successProperty);
1939                 }
1940                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1941                 if (s.id) {
1942                         var g = this.getJsonAccessor(s.id);
1943                         this.getId = function(rec) {
1944                                 var r = g(rec);  
1945                                 return (r === undefined || r === "") ? null : r;
1946                         };
1947                 } else {
1948                         this.getId = function(){return null;};
1949                 }
1950             this.ef = [];
1951             for(var jj = 0; jj < fl; jj++){
1952                 f = fi[jj];
1953                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1954                 this.ef[jj] = this.getJsonAccessor(map);
1955             }
1956         }
1957
1958         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1959         if(s.totalProperty){
1960             var vt = parseInt(this.getTotal(o), 10);
1961             if(!isNaN(vt)){
1962                 totalRecords = vt;
1963             }
1964         }
1965         if(s.successProperty){
1966             var vs = this.getSuccess(o);
1967             if(vs === false || vs === 'false'){
1968                 success = false;
1969             }
1970         }
1971         var records = [];
1972         for(var i = 0; i < c; i++){
1973                 var n = root[i];
1974             var values = {};
1975             var id = this.getId(n);
1976             for(var j = 0; j < fl; j++){
1977                 f = fi[j];
1978             var v = this.ef[j](n);
1979             if (!f.convert) {
1980                 Roo.log('missing convert for ' + f.name);
1981                 Roo.log(f);
1982                 continue;
1983             }
1984             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1985             }
1986             var record = new Record(values, id);
1987             record.json = n;
1988             records[i] = record;
1989         }
1990         return {
1991             raw : o,
1992             success : success,
1993             records : records,
1994             totalRecords : totalRecords
1995         };
1996     },
1997     // used when loading children.. @see loadDataFromChildren
1998     toLoadData: function(rec)
1999     {
2000         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2001         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2002         return { data : data, total : data.length };
2003         
2004     }
2005 });/*
2006  * Based on:
2007  * Ext JS Library 1.1.1
2008  * Copyright(c) 2006-2007, Ext JS, LLC.
2009  *
2010  * Originally Released Under LGPL - original licence link has changed is not relivant.
2011  *
2012  * Fork - LGPL
2013  * <script type="text/javascript">
2014  */
2015
2016 /**
2017  * @class Roo.data.XmlReader
2018  * @extends Roo.data.DataReader
2019  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
2020  * based on mappings in a provided Roo.data.Record constructor.<br><br>
2021  * <p>
2022  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
2023  * header in the HTTP response must be set to "text/xml".</em>
2024  * <p>
2025  * Example code:
2026  * <pre><code>
2027 var RecordDef = Roo.data.Record.create([
2028    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
2029    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
2030 ]);
2031 var myReader = new Roo.data.XmlReader({
2032    totalRecords: "results", // The element which contains the total dataset size (optional)
2033    record: "row",           // The repeated element which contains row information
2034    id: "id"                 // The element within the row that provides an ID for the record (optional)
2035 }, RecordDef);
2036 </code></pre>
2037  * <p>
2038  * This would consume an XML file like this:
2039  * <pre><code>
2040 &lt;?xml?>
2041 &lt;dataset>
2042  &lt;results>2&lt;/results>
2043  &lt;row>
2044    &lt;id>1&lt;/id>
2045    &lt;name>Bill&lt;/name>
2046    &lt;occupation>Gardener&lt;/occupation>
2047  &lt;/row>
2048  &lt;row>
2049    &lt;id>2&lt;/id>
2050    &lt;name>Ben&lt;/name>
2051    &lt;occupation>Horticulturalist&lt;/occupation>
2052  &lt;/row>
2053 &lt;/dataset>
2054 </code></pre>
2055  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2056  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2057  * paged from the remote server.
2058  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2059  * @cfg {String} success The DomQuery path to the success attribute used by forms.
2060  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2061  * a record identifier value.
2062  * @constructor
2063  * Create a new XmlReader
2064  * @param {Object} meta Metadata configuration options
2065  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
2066  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2067  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
2068  */
2069 Roo.data.XmlReader = function(meta, recordType){
2070     meta = meta || {};
2071     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2072 };
2073 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2074     
2075     readerType : 'Xml',
2076     
2077     /**
2078      * This method is only used by a DataProxy which has retrieved data from a remote server.
2079          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
2080          * to contain a method called 'responseXML' that returns an XML document object.
2081      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2082      * a cache of Roo.data.Records.
2083      */
2084     read : function(response){
2085         var doc = response.responseXML;
2086         if(!doc) {
2087             throw {message: "XmlReader.read: XML Document not available"};
2088         }
2089         return this.readRecords(doc);
2090     },
2091
2092     /**
2093      * Create a data block containing Roo.data.Records from an XML document.
2094          * @param {Object} doc A parsed XML document.
2095      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2096      * a cache of Roo.data.Records.
2097      */
2098     readRecords : function(doc){
2099         /**
2100          * After any data loads/reads, the raw XML Document is available for further custom processing.
2101          * @type XMLDocument
2102          */
2103         this.xmlData = doc;
2104         var root = doc.documentElement || doc;
2105         var q = Roo.DomQuery;
2106         var recordType = this.recordType, fields = recordType.prototype.fields;
2107         var sid = this.meta.id;
2108         var totalRecords = 0, success = true;
2109         if(this.meta.totalRecords){
2110             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2111         }
2112         
2113         if(this.meta.success){
2114             var sv = q.selectValue(this.meta.success, root, true);
2115             success = sv !== false && sv !== 'false';
2116         }
2117         var records = [];
2118         var ns = q.select(this.meta.record, root);
2119         for(var i = 0, len = ns.length; i < len; i++) {
2120                 var n = ns[i];
2121                 var values = {};
2122                 var id = sid ? q.selectValue(sid, n) : undefined;
2123                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2124                     var f = fields.items[j];
2125                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2126                     v = f.convert(v);
2127                     values[f.name] = v;
2128                 }
2129                 var record = new recordType(values, id);
2130                 record.node = n;
2131                 records[records.length] = record;
2132             }
2133
2134             return {
2135                 success : success,
2136                 records : records,
2137                 totalRecords : totalRecords || records.length
2138             };
2139     }
2140 });/*
2141  * Based on:
2142  * Ext JS Library 1.1.1
2143  * Copyright(c) 2006-2007, Ext JS, LLC.
2144  *
2145  * Originally Released Under LGPL - original licence link has changed is not relivant.
2146  *
2147  * Fork - LGPL
2148  * <script type="text/javascript">
2149  */
2150
2151 /**
2152  * @class Roo.data.ArrayReader
2153  * @extends Roo.data.DataReader
2154  * Data reader class to create an Array of Roo.data.Record objects from an Array.
2155  * Each element of that Array represents a row of data fields. The
2156  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2157  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2158  * <p>
2159  * Example code:.
2160  * <pre><code>
2161 var RecordDef = Roo.data.Record.create([
2162     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
2163     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
2164 ]);
2165 var myReader = new Roo.data.ArrayReader({
2166     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
2167 }, RecordDef);
2168 </code></pre>
2169  * <p>
2170  * This would consume an Array like this:
2171  * <pre><code>
2172 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2173   </code></pre>
2174  
2175  * @constructor
2176  * Create a new JsonReader
2177  * @param {Object} meta Metadata configuration options.
2178  * @param {Object|Array} recordType Either an Array of field definition objects
2179  * 
2180  * @cfg {Array} fields Array of field definition objects
2181  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2182  * as specified to {@link Roo.data.Record#create},
2183  * or an {@link Roo.data.Record} object
2184  *
2185  * 
2186  * created using {@link Roo.data.Record#create}.
2187  */
2188 Roo.data.ArrayReader = function(meta, recordType)
2189 {    
2190     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2191 };
2192
2193 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2194     
2195       /**
2196      * Create a data block containing Roo.data.Records from an XML document.
2197      * @param {Object} o An Array of row objects which represents the dataset.
2198      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2199      * a cache of Roo.data.Records.
2200      */
2201     readRecords : function(o)
2202     {
2203         var sid = this.meta ? this.meta.id : null;
2204         var recordType = this.recordType, fields = recordType.prototype.fields;
2205         var records = [];
2206         var root = o;
2207         for(var i = 0; i < root.length; i++){
2208             var n = root[i];
2209             var values = {};
2210             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2211             for(var j = 0, jlen = fields.length; j < jlen; j++){
2212                 var f = fields.items[j];
2213                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2214                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2215                 v = f.convert(v);
2216                 values[f.name] = v;
2217             }
2218             var record = new recordType(values, id);
2219             record.json = n;
2220             records[records.length] = record;
2221         }
2222         return {
2223             records : records,
2224             totalRecords : records.length
2225         };
2226     },
2227     // used when loading children.. @see loadDataFromChildren
2228     toLoadData: function(rec)
2229     {
2230         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2231         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2232         
2233     }
2234     
2235     
2236 });/*
2237  * Based on:
2238  * Ext JS Library 1.1.1
2239  * Copyright(c) 2006-2007, Ext JS, LLC.
2240  *
2241  * Originally Released Under LGPL - original licence link has changed is not relivant.
2242  *
2243  * Fork - LGPL
2244  * <script type="text/javascript">
2245  */
2246
2247
2248 /**
2249  * @class Roo.data.Tree
2250  * @extends Roo.util.Observable
2251  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2252  * in the tree have most standard DOM functionality.
2253  * @constructor
2254  * @param {Node} root (optional) The root node
2255  */
2256 Roo.data.Tree = function(root){
2257    this.nodeHash = {};
2258    /**
2259     * The root node for this tree
2260     * @type Node
2261     */
2262    this.root = null;
2263    if(root){
2264        this.setRootNode(root);
2265    }
2266    this.addEvents({
2267        /**
2268         * @event append
2269         * Fires when a new child node is appended to a node in this tree.
2270         * @param {Tree} tree The owner tree
2271         * @param {Node} parent The parent node
2272         * @param {Node} node The newly appended node
2273         * @param {Number} index The index of the newly appended node
2274         */
2275        "append" : true,
2276        /**
2277         * @event remove
2278         * Fires when a child node is removed from a node in this tree.
2279         * @param {Tree} tree The owner tree
2280         * @param {Node} parent The parent node
2281         * @param {Node} node The child node removed
2282         */
2283        "remove" : true,
2284        /**
2285         * @event move
2286         * Fires when a node is moved to a new location in the tree
2287         * @param {Tree} tree The owner tree
2288         * @param {Node} node The node moved
2289         * @param {Node} oldParent The old parent of this node
2290         * @param {Node} newParent The new parent of this node
2291         * @param {Number} index The index it was moved to
2292         */
2293        "move" : true,
2294        /**
2295         * @event insert
2296         * Fires when a new child node is inserted in a node in this tree.
2297         * @param {Tree} tree The owner tree
2298         * @param {Node} parent The parent node
2299         * @param {Node} node The child node inserted
2300         * @param {Node} refNode The child node the node was inserted before
2301         */
2302        "insert" : true,
2303        /**
2304         * @event beforeappend
2305         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2306         * @param {Tree} tree The owner tree
2307         * @param {Node} parent The parent node
2308         * @param {Node} node The child node to be appended
2309         */
2310        "beforeappend" : true,
2311        /**
2312         * @event beforeremove
2313         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2314         * @param {Tree} tree The owner tree
2315         * @param {Node} parent The parent node
2316         * @param {Node} node The child node to be removed
2317         */
2318        "beforeremove" : true,
2319        /**
2320         * @event beforemove
2321         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2322         * @param {Tree} tree The owner tree
2323         * @param {Node} node The node being moved
2324         * @param {Node} oldParent The parent of the node
2325         * @param {Node} newParent The new parent the node is moving to
2326         * @param {Number} index The index it is being moved to
2327         */
2328        "beforemove" : true,
2329        /**
2330         * @event beforeinsert
2331         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2332         * @param {Tree} tree The owner tree
2333         * @param {Node} parent The parent node
2334         * @param {Node} node The child node to be inserted
2335         * @param {Node} refNode The child node the node is being inserted before
2336         */
2337        "beforeinsert" : true
2338    });
2339
2340     Roo.data.Tree.superclass.constructor.call(this);
2341 };
2342
2343 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2344     pathSeparator: "/",
2345
2346     proxyNodeEvent : function(){
2347         return this.fireEvent.apply(this, arguments);
2348     },
2349
2350     /**
2351      * Returns the root node for this tree.
2352      * @return {Node}
2353      */
2354     getRootNode : function(){
2355         return this.root;
2356     },
2357
2358     /**
2359      * Sets the root node for this tree.
2360      * @param {Node} node
2361      * @return {Node}
2362      */
2363     setRootNode : function(node){
2364         this.root = node;
2365         node.ownerTree = this;
2366         node.isRoot = true;
2367         this.registerNode(node);
2368         return node;
2369     },
2370
2371     /**
2372      * Gets a node in this tree by its id.
2373      * @param {String} id
2374      * @return {Node}
2375      */
2376     getNodeById : function(id){
2377         return this.nodeHash[id];
2378     },
2379
2380     registerNode : function(node){
2381         this.nodeHash[node.id] = node;
2382     },
2383
2384     unregisterNode : function(node){
2385         delete this.nodeHash[node.id];
2386     },
2387
2388     toString : function(){
2389         return "[Tree"+(this.id?" "+this.id:"")+"]";
2390     }
2391 });
2392
2393 /**
2394  * @class Roo.data.Node
2395  * @extends Roo.util.Observable
2396  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2397  * @cfg {String} id The id for this node. If one is not specified, one is generated.
2398  * @constructor
2399  * @param {Object} attributes The attributes/config for the node
2400  */
2401 Roo.data.Node = function(attributes){
2402     /**
2403      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2404      * @type {Object}
2405      */
2406     this.attributes = attributes || {};
2407     this.leaf = this.attributes.leaf;
2408     /**
2409      * The node id. @type String
2410      */
2411     this.id = this.attributes.id;
2412     if(!this.id){
2413         this.id = Roo.id(null, "ynode-");
2414         this.attributes.id = this.id;
2415     }
2416      
2417     
2418     /**
2419      * All child nodes of this node. @type Array
2420      */
2421     this.childNodes = [];
2422     if(!this.childNodes.indexOf){ // indexOf is a must
2423         this.childNodes.indexOf = function(o){
2424             for(var i = 0, len = this.length; i < len; i++){
2425                 if(this[i] == o) {
2426                     return i;
2427                 }
2428             }
2429             return -1;
2430         };
2431     }
2432     /**
2433      * The parent node for this node. @type Node
2434      */
2435     this.parentNode = null;
2436     /**
2437      * The first direct child node of this node, or null if this node has no child nodes. @type Node
2438      */
2439     this.firstChild = null;
2440     /**
2441      * The last direct child node of this node, or null if this node has no child nodes. @type Node
2442      */
2443     this.lastChild = null;
2444     /**
2445      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2446      */
2447     this.previousSibling = null;
2448     /**
2449      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2450      */
2451     this.nextSibling = null;
2452
2453     this.addEvents({
2454        /**
2455         * @event append
2456         * Fires when a new child node is appended
2457         * @param {Tree} tree The owner tree
2458         * @param {Node} this This node
2459         * @param {Node} node The newly appended node
2460         * @param {Number} index The index of the newly appended node
2461         */
2462        "append" : true,
2463        /**
2464         * @event remove
2465         * Fires when a child node is removed
2466         * @param {Tree} tree The owner tree
2467         * @param {Node} this This node
2468         * @param {Node} node The removed node
2469         */
2470        "remove" : true,
2471        /**
2472         * @event move
2473         * Fires when this node is moved to a new location in the tree
2474         * @param {Tree} tree The owner tree
2475         * @param {Node} this This node
2476         * @param {Node} oldParent The old parent of this node
2477         * @param {Node} newParent The new parent of this node
2478         * @param {Number} index The index it was moved to
2479         */
2480        "move" : true,
2481        /**
2482         * @event insert
2483         * Fires when a new child node is inserted.
2484         * @param {Tree} tree The owner tree
2485         * @param {Node} this This node
2486         * @param {Node} node The child node inserted
2487         * @param {Node} refNode The child node the node was inserted before
2488         */
2489        "insert" : true,
2490        /**
2491         * @event beforeappend
2492         * Fires before a new child is appended, return false to cancel the append.
2493         * @param {Tree} tree The owner tree
2494         * @param {Node} this This node
2495         * @param {Node} node The child node to be appended
2496         */
2497        "beforeappend" : true,
2498        /**
2499         * @event beforeremove
2500         * Fires before a child is removed, return false to cancel the remove.
2501         * @param {Tree} tree The owner tree
2502         * @param {Node} this This node
2503         * @param {Node} node The child node to be removed
2504         */
2505        "beforeremove" : true,
2506        /**
2507         * @event beforemove
2508         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2509         * @param {Tree} tree The owner tree
2510         * @param {Node} this This node
2511         * @param {Node} oldParent The parent of this node
2512         * @param {Node} newParent The new parent this node is moving to
2513         * @param {Number} index The index it is being moved to
2514         */
2515        "beforemove" : true,
2516        /**
2517         * @event beforeinsert
2518         * Fires before a new child is inserted, return false to cancel the insert.
2519         * @param {Tree} tree The owner tree
2520         * @param {Node} this This node
2521         * @param {Node} node The child node to be inserted
2522         * @param {Node} refNode The child node the node is being inserted before
2523         */
2524        "beforeinsert" : true
2525    });
2526     this.listeners = this.attributes.listeners;
2527     Roo.data.Node.superclass.constructor.call(this);
2528 };
2529
2530 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2531     fireEvent : function(evtName){
2532         // first do standard event for this node
2533         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2534             return false;
2535         }
2536         // then bubble it up to the tree if the event wasn't cancelled
2537         var ot = this.getOwnerTree();
2538         if(ot){
2539             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2540                 return false;
2541             }
2542         }
2543         return true;
2544     },
2545
2546     /**
2547      * Returns true if this node is a leaf
2548      * @return {Boolean}
2549      */
2550     isLeaf : function(){
2551         return this.leaf === true;
2552     },
2553
2554     // private
2555     setFirstChild : function(node){
2556         this.firstChild = node;
2557     },
2558
2559     //private
2560     setLastChild : function(node){
2561         this.lastChild = node;
2562     },
2563
2564
2565     /**
2566      * Returns true if this node is the last child of its parent
2567      * @return {Boolean}
2568      */
2569     isLast : function(){
2570        return (!this.parentNode ? true : this.parentNode.lastChild == this);
2571     },
2572
2573     /**
2574      * Returns true if this node is the first child of its parent
2575      * @return {Boolean}
2576      */
2577     isFirst : function(){
2578        return (!this.parentNode ? true : this.parentNode.firstChild == this);
2579     },
2580
2581     hasChildNodes : function(){
2582         return !this.isLeaf() && this.childNodes.length > 0;
2583     },
2584
2585     /**
2586      * Insert node(s) as the last child node of this node.
2587      * @param {Node/Array} node The node or Array of nodes to append
2588      * @return {Node} The appended node if single append, or null if an array was passed
2589      */
2590     appendChild : function(node){
2591         var multi = false;
2592         if(node instanceof Array){
2593             multi = node;
2594         }else if(arguments.length > 1){
2595             multi = arguments;
2596         }
2597         
2598         // if passed an array or multiple args do them one by one
2599         if(multi){
2600             for(var i = 0, len = multi.length; i < len; i++) {
2601                 this.appendChild(multi[i]);
2602             }
2603         }else{
2604             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2605                 return false;
2606             }
2607             var index = this.childNodes.length;
2608             var oldParent = node.parentNode;
2609             // it's a move, make sure we move it cleanly
2610             if(oldParent){
2611                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2612                     return false;
2613                 }
2614                 oldParent.removeChild(node);
2615             }
2616             
2617             index = this.childNodes.length;
2618             if(index == 0){
2619                 this.setFirstChild(node);
2620             }
2621             this.childNodes.push(node);
2622             node.parentNode = this;
2623             var ps = this.childNodes[index-1];
2624             if(ps){
2625                 node.previousSibling = ps;
2626                 ps.nextSibling = node;
2627             }else{
2628                 node.previousSibling = null;
2629             }
2630             node.nextSibling = null;
2631             this.setLastChild(node);
2632             node.setOwnerTree(this.getOwnerTree());
2633             this.fireEvent("append", this.ownerTree, this, node, index);
2634             if(this.ownerTree) {
2635                 this.ownerTree.fireEvent("appendnode", this, node, index);
2636             }
2637             if(oldParent){
2638                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2639             }
2640             return node;
2641         }
2642     },
2643
2644     /**
2645      * Removes a child node from this node.
2646      * @param {Node} node The node to remove
2647      * @return {Node} The removed node
2648      */
2649     removeChild : function(node){
2650         var index = this.childNodes.indexOf(node);
2651         if(index == -1){
2652             return false;
2653         }
2654         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2655             return false;
2656         }
2657
2658         // remove it from childNodes collection
2659         this.childNodes.splice(index, 1);
2660
2661         // update siblings
2662         if(node.previousSibling){
2663             node.previousSibling.nextSibling = node.nextSibling;
2664         }
2665         if(node.nextSibling){
2666             node.nextSibling.previousSibling = node.previousSibling;
2667         }
2668
2669         // update child refs
2670         if(this.firstChild == node){
2671             this.setFirstChild(node.nextSibling);
2672         }
2673         if(this.lastChild == node){
2674             this.setLastChild(node.previousSibling);
2675         }
2676
2677         node.setOwnerTree(null);
2678         // clear any references from the node
2679         node.parentNode = null;
2680         node.previousSibling = null;
2681         node.nextSibling = null;
2682         this.fireEvent("remove", this.ownerTree, this, node);
2683         return node;
2684     },
2685
2686     /**
2687      * Inserts the first node before the second node in this nodes childNodes collection.
2688      * @param {Node} node The node to insert
2689      * @param {Node} refNode The node to insert before (if null the node is appended)
2690      * @return {Node} The inserted node
2691      */
2692     insertBefore : function(node, refNode){
2693         if(!refNode){ // like standard Dom, refNode can be null for append
2694             return this.appendChild(node);
2695         }
2696         // nothing to do
2697         if(node == refNode){
2698             return false;
2699         }
2700
2701         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2702             return false;
2703         }
2704         var index = this.childNodes.indexOf(refNode);
2705         var oldParent = node.parentNode;
2706         var refIndex = index;
2707
2708         // when moving internally, indexes will change after remove
2709         if(oldParent == this && this.childNodes.indexOf(node) < index){
2710             refIndex--;
2711         }
2712
2713         // it's a move, make sure we move it cleanly
2714         if(oldParent){
2715             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2716                 return false;
2717             }
2718             oldParent.removeChild(node);
2719         }
2720         if(refIndex == 0){
2721             this.setFirstChild(node);
2722         }
2723         this.childNodes.splice(refIndex, 0, node);
2724         node.parentNode = this;
2725         var ps = this.childNodes[refIndex-1];
2726         if(ps){
2727             node.previousSibling = ps;
2728             ps.nextSibling = node;
2729         }else{
2730             node.previousSibling = null;
2731         }
2732         node.nextSibling = refNode;
2733         refNode.previousSibling = node;
2734         node.setOwnerTree(this.getOwnerTree());
2735         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2736         if(oldParent){
2737             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2738         }
2739         return node;
2740     },
2741
2742     /**
2743      * Returns the child node at the specified index.
2744      * @param {Number} index
2745      * @return {Node}
2746      */
2747     item : function(index){
2748         return this.childNodes[index];
2749     },
2750
2751     /**
2752      * Replaces one child node in this node with another.
2753      * @param {Node} newChild The replacement node
2754      * @param {Node} oldChild The node to replace
2755      * @return {Node} The replaced node
2756      */
2757     replaceChild : function(newChild, oldChild){
2758         this.insertBefore(newChild, oldChild);
2759         this.removeChild(oldChild);
2760         return oldChild;
2761     },
2762
2763     /**
2764      * Returns the index of a child node
2765      * @param {Node} node
2766      * @return {Number} The index of the node or -1 if it was not found
2767      */
2768     indexOf : function(child){
2769         return this.childNodes.indexOf(child);
2770     },
2771
2772     /**
2773      * Returns the tree this node is in.
2774      * @return {Tree}
2775      */
2776     getOwnerTree : function(){
2777         // if it doesn't have one, look for one
2778         if(!this.ownerTree){
2779             var p = this;
2780             while(p){
2781                 if(p.ownerTree){
2782                     this.ownerTree = p.ownerTree;
2783                     break;
2784                 }
2785                 p = p.parentNode;
2786             }
2787         }
2788         return this.ownerTree;
2789     },
2790
2791     /**
2792      * Returns depth of this node (the root node has a depth of 0)
2793      * @return {Number}
2794      */
2795     getDepth : function(){
2796         var depth = 0;
2797         var p = this;
2798         while(p.parentNode){
2799             ++depth;
2800             p = p.parentNode;
2801         }
2802         return depth;
2803     },
2804
2805     // private
2806     setOwnerTree : function(tree){
2807         // if it's move, we need to update everyone
2808         if(tree != this.ownerTree){
2809             if(this.ownerTree){
2810                 this.ownerTree.unregisterNode(this);
2811             }
2812             this.ownerTree = tree;
2813             var cs = this.childNodes;
2814             for(var i = 0, len = cs.length; i < len; i++) {
2815                 cs[i].setOwnerTree(tree);
2816             }
2817             if(tree){
2818                 tree.registerNode(this);
2819             }
2820         }
2821     },
2822
2823     /**
2824      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2825      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2826      * @return {String} The path
2827      */
2828     getPath : function(attr){
2829         attr = attr || "id";
2830         var p = this.parentNode;
2831         var b = [this.attributes[attr]];
2832         while(p){
2833             b.unshift(p.attributes[attr]);
2834             p = p.parentNode;
2835         }
2836         var sep = this.getOwnerTree().pathSeparator;
2837         return sep + b.join(sep);
2838     },
2839
2840     /**
2841      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2842      * function call will be the scope provided or the current node. The arguments to the function
2843      * will be the args provided or the current node. If the function returns false at any point,
2844      * the bubble is stopped.
2845      * @param {Function} fn The function to call
2846      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2847      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2848      */
2849     bubble : function(fn, scope, args){
2850         var p = this;
2851         while(p){
2852             if(fn.call(scope || p, args || p) === false){
2853                 break;
2854             }
2855             p = p.parentNode;
2856         }
2857     },
2858
2859     /**
2860      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2861      * function call will be the scope provided or the current node. The arguments to the function
2862      * will be the args provided or the current node. If the function returns false at any point,
2863      * the cascade is stopped on that branch.
2864      * @param {Function} fn The function to call
2865      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2866      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2867      */
2868     cascade : function(fn, scope, args){
2869         if(fn.call(scope || this, args || this) !== false){
2870             var cs = this.childNodes;
2871             for(var i = 0, len = cs.length; i < len; i++) {
2872                 cs[i].cascade(fn, scope, args);
2873             }
2874         }
2875     },
2876
2877     /**
2878      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2879      * function call will be the scope provided or the current node. The arguments to the function
2880      * will be the args provided or the current node. If the function returns false at any point,
2881      * the iteration stops.
2882      * @param {Function} fn The function to call
2883      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2884      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2885      */
2886     eachChild : function(fn, scope, args){
2887         var cs = this.childNodes;
2888         for(var i = 0, len = cs.length; i < len; i++) {
2889                 if(fn.call(scope || this, args || cs[i]) === false){
2890                     break;
2891                 }
2892         }
2893     },
2894
2895     /**
2896      * Finds the first child that has the attribute with the specified value.
2897      * @param {String} attribute The attribute name
2898      * @param {Mixed} value The value to search for
2899      * @return {Node} The found child or null if none was found
2900      */
2901     findChild : function(attribute, value){
2902         var cs = this.childNodes;
2903         for(var i = 0, len = cs.length; i < len; i++) {
2904                 if(cs[i].attributes[attribute] == value){
2905                     return cs[i];
2906                 }
2907         }
2908         return null;
2909     },
2910
2911     /**
2912      * Finds the first child by a custom function. The child matches if the function passed
2913      * returns true.
2914      * @param {Function} fn
2915      * @param {Object} scope (optional)
2916      * @return {Node} The found child or null if none was found
2917      */
2918     findChildBy : function(fn, scope){
2919         var cs = this.childNodes;
2920         for(var i = 0, len = cs.length; i < len; i++) {
2921                 if(fn.call(scope||cs[i], cs[i]) === true){
2922                     return cs[i];
2923                 }
2924         }
2925         return null;
2926     },
2927
2928     /**
2929      * Sorts this nodes children using the supplied sort function
2930      * @param {Function} fn
2931      * @param {Object} scope (optional)
2932      */
2933     sort : function(fn, scope){
2934         var cs = this.childNodes;
2935         var len = cs.length;
2936         if(len > 0){
2937             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2938             cs.sort(sortFn);
2939             for(var i = 0; i < len; i++){
2940                 var n = cs[i];
2941                 n.previousSibling = cs[i-1];
2942                 n.nextSibling = cs[i+1];
2943                 if(i == 0){
2944                     this.setFirstChild(n);
2945                 }
2946                 if(i == len-1){
2947                     this.setLastChild(n);
2948                 }
2949             }
2950         }
2951     },
2952
2953     /**
2954      * Returns true if this node is an ancestor (at any point) of the passed node.
2955      * @param {Node} node
2956      * @return {Boolean}
2957      */
2958     contains : function(node){
2959         return node.isAncestor(this);
2960     },
2961
2962     /**
2963      * Returns true if the passed node is an ancestor (at any point) of this node.
2964      * @param {Node} node
2965      * @return {Boolean}
2966      */
2967     isAncestor : function(node){
2968         var p = this.parentNode;
2969         while(p){
2970             if(p == node){
2971                 return true;
2972             }
2973             p = p.parentNode;
2974         }
2975         return false;
2976     },
2977
2978     toString : function(){
2979         return "[Node"+(this.id?" "+this.id:"")+"]";
2980     }
2981 });/*
2982  * Based on:
2983  * Ext JS Library 1.1.1
2984  * Copyright(c) 2006-2007, Ext JS, LLC.
2985  *
2986  * Originally Released Under LGPL - original licence link has changed is not relivant.
2987  *
2988  * Fork - LGPL
2989  * <script type="text/javascript">
2990  */
2991
2992
2993 /**
2994  * @class Roo.Shadow
2995  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
2996  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
2997  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
2998  * @constructor
2999  * Create a new Shadow
3000  * @param {Object} config The config object
3001  */
3002 Roo.Shadow = function(config){
3003     Roo.apply(this, config);
3004     if(typeof this.mode != "string"){
3005         this.mode = this.defaultMode;
3006     }
3007     var o = this.offset, a = {h: 0};
3008     var rad = Math.floor(this.offset/2);
3009     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3010         case "drop":
3011             a.w = 0;
3012             a.l = a.t = o;
3013             a.t -= 1;
3014             if(Roo.isIE){
3015                 a.l -= this.offset + rad;
3016                 a.t -= this.offset + rad;
3017                 a.w -= rad;
3018                 a.h -= rad;
3019                 a.t += 1;
3020             }
3021         break;
3022         case "sides":
3023             a.w = (o*2);
3024             a.l = -o;
3025             a.t = o-1;
3026             if(Roo.isIE){
3027                 a.l -= (this.offset - rad);
3028                 a.t -= this.offset + rad;
3029                 a.l += 1;
3030                 a.w -= (this.offset - rad)*2;
3031                 a.w -= rad + 1;
3032                 a.h -= 1;
3033             }
3034         break;
3035         case "frame":
3036             a.w = a.h = (o*2);
3037             a.l = a.t = -o;
3038             a.t += 1;
3039             a.h -= 2;
3040             if(Roo.isIE){
3041                 a.l -= (this.offset - rad);
3042                 a.t -= (this.offset - rad);
3043                 a.l += 1;
3044                 a.w -= (this.offset + rad + 1);
3045                 a.h -= (this.offset + rad);
3046                 a.h += 1;
3047             }
3048         break;
3049     };
3050
3051     this.adjusts = a;
3052 };
3053
3054 Roo.Shadow.prototype = {
3055     /**
3056      * @cfg {String} mode
3057      * The shadow display mode.  Supports the following options:<br />
3058      * sides: Shadow displays on both sides and bottom only<br />
3059      * frame: Shadow displays equally on all four sides<br />
3060      * drop: Traditional bottom-right drop shadow (default)
3061      */
3062     mode: false,
3063     /**
3064      * @cfg {String} offset
3065      * The number of pixels to offset the shadow from the element (defaults to 4)
3066      */
3067     offset: 4,
3068
3069     // private
3070     defaultMode: "drop",
3071
3072     /**
3073      * Displays the shadow under the target element
3074      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3075      */
3076     show : function(target){
3077         target = Roo.get(target);
3078         if(!this.el){
3079             this.el = Roo.Shadow.Pool.pull();
3080             if(this.el.dom.nextSibling != target.dom){
3081                 this.el.insertBefore(target);
3082             }
3083         }
3084         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3085         if(Roo.isIE){
3086             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3087         }
3088         this.realign(
3089             target.getLeft(true),
3090             target.getTop(true),
3091             target.getWidth(),
3092             target.getHeight()
3093         );
3094         this.el.dom.style.display = "block";
3095     },
3096
3097     /**
3098      * Returns true if the shadow is visible, else false
3099      */
3100     isVisible : function(){
3101         return this.el ? true : false;  
3102     },
3103
3104     /**
3105      * Direct alignment when values are already available. Show must be called at least once before
3106      * calling this method to ensure it is initialized.
3107      * @param {Number} left The target element left position
3108      * @param {Number} top The target element top position
3109      * @param {Number} width The target element width
3110      * @param {Number} height The target element height
3111      */
3112     realign : function(l, t, w, h){
3113         if(!this.el){
3114             return;
3115         }
3116         var a = this.adjusts, d = this.el.dom, s = d.style;
3117         var iea = 0;
3118         s.left = (l+a.l)+"px";
3119         s.top = (t+a.t)+"px";
3120         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3121  
3122         if(s.width != sws || s.height != shs){
3123             s.width = sws;
3124             s.height = shs;
3125             if(!Roo.isIE){
3126                 var cn = d.childNodes;
3127                 var sww = Math.max(0, (sw-12))+"px";
3128                 cn[0].childNodes[1].style.width = sww;
3129                 cn[1].childNodes[1].style.width = sww;
3130                 cn[2].childNodes[1].style.width = sww;
3131                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3132             }
3133         }
3134     },
3135
3136     /**
3137      * Hides this shadow
3138      */
3139     hide : function(){
3140         if(this.el){
3141             this.el.dom.style.display = "none";
3142             Roo.Shadow.Pool.push(this.el);
3143             delete this.el;
3144         }
3145     },
3146
3147     /**
3148      * Adjust the z-index of this shadow
3149      * @param {Number} zindex The new z-index
3150      */
3151     setZIndex : function(z){
3152         this.zIndex = z;
3153         if(this.el){
3154             this.el.setStyle("z-index", z);
3155         }
3156     }
3157 };
3158
3159 // Private utility class that manages the internal Shadow cache
3160 Roo.Shadow.Pool = function(){
3161     var p = [];
3162     var markup = Roo.isIE ?
3163                  '<div class="x-ie-shadow"></div>' :
3164                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
3165     return {
3166         pull : function(){
3167             var sh = p.shift();
3168             if(!sh){
3169                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3170                 sh.autoBoxAdjust = false;
3171             }
3172             return sh;
3173         },
3174
3175         push : function(sh){
3176             p.push(sh);
3177         }
3178     };
3179 }();/*
3180  * Based on:
3181  * Ext JS Library 1.1.1
3182  * Copyright(c) 2006-2007, Ext JS, LLC.
3183  *
3184  * Originally Released Under LGPL - original licence link has changed is not relivant.
3185  *
3186  * Fork - LGPL
3187  * <script type="text/javascript">
3188  */
3189
3190
3191 /**
3192  * @class Roo.SplitBar
3193  * @extends Roo.util.Observable
3194  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3195  * <br><br>
3196  * Usage:
3197  * <pre><code>
3198 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3199                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3200 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3201 split.minSize = 100;
3202 split.maxSize = 600;
3203 split.animate = true;
3204 split.on('moved', splitterMoved);
3205 </code></pre>
3206  * @constructor
3207  * Create a new SplitBar
3208  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3209  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3210  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3211  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3212                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3213                         position of the SplitBar).
3214  */
3215 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3216     
3217     /** @private */
3218     this.el = Roo.get(dragElement, true);
3219     this.el.dom.unselectable = "on";
3220     /** @private */
3221     this.resizingEl = Roo.get(resizingElement, true);
3222
3223     /**
3224      * @private
3225      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3226      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3227      * @type Number
3228      */
3229     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3230     
3231     /**
3232      * The minimum size of the resizing element. (Defaults to 0)
3233      * @type Number
3234      */
3235     this.minSize = 0;
3236     
3237     /**
3238      * The maximum size of the resizing element. (Defaults to 2000)
3239      * @type Number
3240      */
3241     this.maxSize = 2000;
3242     
3243     /**
3244      * Whether to animate the transition to the new size
3245      * @type Boolean
3246      */
3247     this.animate = false;
3248     
3249     /**
3250      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3251      * @type Boolean
3252      */
3253     this.useShim = false;
3254     
3255     /** @private */
3256     this.shim = null;
3257     
3258     if(!existingProxy){
3259         /** @private */
3260         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3261     }else{
3262         this.proxy = Roo.get(existingProxy).dom;
3263     }
3264     /** @private */
3265     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3266     
3267     /** @private */
3268     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3269     
3270     /** @private */
3271     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3272     
3273     /** @private */
3274     this.dragSpecs = {};
3275     
3276     /**
3277      * @private The adapter to use to positon and resize elements
3278      */
3279     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3280     this.adapter.init(this);
3281     
3282     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3283         /** @private */
3284         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3285         this.el.addClass("x-splitbar-h");
3286     }else{
3287         /** @private */
3288         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3289         this.el.addClass("x-splitbar-v");
3290     }
3291     
3292     this.addEvents({
3293         /**
3294          * @event resize
3295          * Fires when the splitter is moved (alias for {@link #event-moved})
3296          * @param {Roo.SplitBar} this
3297          * @param {Number} newSize the new width or height
3298          */
3299         "resize" : true,
3300         /**
3301          * @event moved
3302          * Fires when the splitter is moved
3303          * @param {Roo.SplitBar} this
3304          * @param {Number} newSize the new width or height
3305          */
3306         "moved" : true,
3307         /**
3308          * @event beforeresize
3309          * Fires before the splitter is dragged
3310          * @param {Roo.SplitBar} this
3311          */
3312         "beforeresize" : true,
3313
3314         "beforeapply" : true
3315     });
3316
3317     Roo.util.Observable.call(this);
3318 };
3319
3320 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3321     onStartProxyDrag : function(x, y){
3322         this.fireEvent("beforeresize", this);
3323         if(!this.overlay){
3324             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3325             o.unselectable();
3326             o.enableDisplayMode("block");
3327             // all splitbars share the same overlay
3328             Roo.SplitBar.prototype.overlay = o;
3329         }
3330         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3331         this.overlay.show();
3332         Roo.get(this.proxy).setDisplayed("block");
3333         var size = this.adapter.getElementSize(this);
3334         this.activeMinSize = this.getMinimumSize();;
3335         this.activeMaxSize = this.getMaximumSize();;
3336         var c1 = size - this.activeMinSize;
3337         var c2 = Math.max(this.activeMaxSize - size, 0);
3338         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3339             this.dd.resetConstraints();
3340             this.dd.setXConstraint(
3341                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3342                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3343             );
3344             this.dd.setYConstraint(0, 0);
3345         }else{
3346             this.dd.resetConstraints();
3347             this.dd.setXConstraint(0, 0);
3348             this.dd.setYConstraint(
3349                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3350                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3351             );
3352          }
3353         this.dragSpecs.startSize = size;
3354         this.dragSpecs.startPoint = [x, y];
3355         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3356     },
3357     
3358     /** 
3359      * @private Called after the drag operation by the DDProxy
3360      */
3361     onEndProxyDrag : function(e){
3362         Roo.get(this.proxy).setDisplayed(false);
3363         var endPoint = Roo.lib.Event.getXY(e);
3364         if(this.overlay){
3365             this.overlay.hide();
3366         }
3367         var newSize;
3368         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3369             newSize = this.dragSpecs.startSize + 
3370                 (this.placement == Roo.SplitBar.LEFT ?
3371                     endPoint[0] - this.dragSpecs.startPoint[0] :
3372                     this.dragSpecs.startPoint[0] - endPoint[0]
3373                 );
3374         }else{
3375             newSize = this.dragSpecs.startSize + 
3376                 (this.placement == Roo.SplitBar.TOP ?
3377                     endPoint[1] - this.dragSpecs.startPoint[1] :
3378                     this.dragSpecs.startPoint[1] - endPoint[1]
3379                 );
3380         }
3381         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3382         if(newSize != this.dragSpecs.startSize){
3383             if(this.fireEvent('beforeapply', this, newSize) !== false){
3384                 this.adapter.setElementSize(this, newSize);
3385                 this.fireEvent("moved", this, newSize);
3386                 this.fireEvent("resize", this, newSize);
3387             }
3388         }
3389     },
3390     
3391     /**
3392      * Get the adapter this SplitBar uses
3393      * @return The adapter object
3394      */
3395     getAdapter : function(){
3396         return this.adapter;
3397     },
3398     
3399     /**
3400      * Set the adapter this SplitBar uses
3401      * @param {Object} adapter A SplitBar adapter object
3402      */
3403     setAdapter : function(adapter){
3404         this.adapter = adapter;
3405         this.adapter.init(this);
3406     },
3407     
3408     /**
3409      * Gets the minimum size for the resizing element
3410      * @return {Number} The minimum size
3411      */
3412     getMinimumSize : function(){
3413         return this.minSize;
3414     },
3415     
3416     /**
3417      * Sets the minimum size for the resizing element
3418      * @param {Number} minSize The minimum size
3419      */
3420     setMinimumSize : function(minSize){
3421         this.minSize = minSize;
3422     },
3423     
3424     /**
3425      * Gets the maximum size for the resizing element
3426      * @return {Number} The maximum size
3427      */
3428     getMaximumSize : function(){
3429         return this.maxSize;
3430     },
3431     
3432     /**
3433      * Sets the maximum size for the resizing element
3434      * @param {Number} maxSize The maximum size
3435      */
3436     setMaximumSize : function(maxSize){
3437         this.maxSize = maxSize;
3438     },
3439     
3440     /**
3441      * Sets the initialize size for the resizing element
3442      * @param {Number} size The initial size
3443      */
3444     setCurrentSize : function(size){
3445         var oldAnimate = this.animate;
3446         this.animate = false;
3447         this.adapter.setElementSize(this, size);
3448         this.animate = oldAnimate;
3449     },
3450     
3451     /**
3452      * Destroy this splitbar. 
3453      * @param {Boolean} removeEl True to remove the element
3454      */
3455     destroy : function(removeEl){
3456         if(this.shim){
3457             this.shim.remove();
3458         }
3459         this.dd.unreg();
3460         this.proxy.parentNode.removeChild(this.proxy);
3461         if(removeEl){
3462             this.el.remove();
3463         }
3464     }
3465 });
3466
3467 /**
3468  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
3469  */
3470 Roo.SplitBar.createProxy = function(dir){
3471     var proxy = new Roo.Element(document.createElement("div"));
3472     proxy.unselectable();
3473     var cls = 'x-splitbar-proxy';
3474     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3475     document.body.appendChild(proxy.dom);
3476     return proxy.dom;
3477 };
3478
3479 /** 
3480  * @class Roo.SplitBar.BasicLayoutAdapter
3481  * Default Adapter. It assumes the splitter and resizing element are not positioned
3482  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3483  */
3484 Roo.SplitBar.BasicLayoutAdapter = function(){
3485 };
3486
3487 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3488     // do nothing for now
3489     init : function(s){
3490     
3491     },
3492     /**
3493      * Called before drag operations to get the current size of the resizing element. 
3494      * @param {Roo.SplitBar} s The SplitBar using this adapter
3495      */
3496      getElementSize : function(s){
3497         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3498             return s.resizingEl.getWidth();
3499         }else{
3500             return s.resizingEl.getHeight();
3501         }
3502     },
3503     
3504     /**
3505      * Called after drag operations to set the size of the resizing element.
3506      * @param {Roo.SplitBar} s The SplitBar using this adapter
3507      * @param {Number} newSize The new size to set
3508      * @param {Function} onComplete A function to be invoked when resizing is complete
3509      */
3510     setElementSize : function(s, newSize, onComplete){
3511         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3512             if(!s.animate){
3513                 s.resizingEl.setWidth(newSize);
3514                 if(onComplete){
3515                     onComplete(s, newSize);
3516                 }
3517             }else{
3518                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3519             }
3520         }else{
3521             
3522             if(!s.animate){
3523                 s.resizingEl.setHeight(newSize);
3524                 if(onComplete){
3525                     onComplete(s, newSize);
3526                 }
3527             }else{
3528                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3529             }
3530         }
3531     }
3532 };
3533
3534 /** 
3535  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3536  * @extends Roo.SplitBar.BasicLayoutAdapter
3537  * Adapter that  moves the splitter element to align with the resized sizing element. 
3538  * Used with an absolute positioned SplitBar.
3539  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3540  * document.body, make sure you assign an id to the body element.
3541  */
3542 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3543     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3544     this.container = Roo.get(container);
3545 };
3546
3547 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3548     init : function(s){
3549         this.basic.init(s);
3550     },
3551     
3552     getElementSize : function(s){
3553         return this.basic.getElementSize(s);
3554     },
3555     
3556     setElementSize : function(s, newSize, onComplete){
3557         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3558     },
3559     
3560     moveSplitter : function(s){
3561         var yes = Roo.SplitBar;
3562         switch(s.placement){
3563             case yes.LEFT:
3564                 s.el.setX(s.resizingEl.getRight());
3565                 break;
3566             case yes.RIGHT:
3567                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3568                 break;
3569             case yes.TOP:
3570                 s.el.setY(s.resizingEl.getBottom());
3571                 break;
3572             case yes.BOTTOM:
3573                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3574                 break;
3575         }
3576     }
3577 };
3578
3579 /**
3580  * Orientation constant - Create a vertical SplitBar
3581  * @static
3582  * @type Number
3583  */
3584 Roo.SplitBar.VERTICAL = 1;
3585
3586 /**
3587  * Orientation constant - Create a horizontal SplitBar
3588  * @static
3589  * @type Number
3590  */
3591 Roo.SplitBar.HORIZONTAL = 2;
3592
3593 /**
3594  * Placement constant - The resizing element is to the left of the splitter element
3595  * @static
3596  * @type Number
3597  */
3598 Roo.SplitBar.LEFT = 1;
3599
3600 /**
3601  * Placement constant - The resizing element is to the right of the splitter element
3602  * @static
3603  * @type Number
3604  */
3605 Roo.SplitBar.RIGHT = 2;
3606
3607 /**
3608  * Placement constant - The resizing element is positioned above the splitter element
3609  * @static
3610  * @type Number
3611  */
3612 Roo.SplitBar.TOP = 3;
3613
3614 /**
3615  * Placement constant - The resizing element is positioned under splitter element
3616  * @static
3617  * @type Number
3618  */
3619 Roo.SplitBar.BOTTOM = 4;
3620 /*
3621  * Based on:
3622  * Ext JS Library 1.1.1
3623  * Copyright(c) 2006-2007, Ext JS, LLC.
3624  *
3625  * Originally Released Under LGPL - original licence link has changed is not relivant.
3626  *
3627  * Fork - LGPL
3628  * <script type="text/javascript">
3629  */
3630
3631 /**
3632  * @class Roo.View
3633  * @extends Roo.util.Observable
3634  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
3635  * This class also supports single and multi selection modes. <br>
3636  * Create a data model bound view:
3637  <pre><code>
3638  var store = new Roo.data.Store(...);
3639
3640  var view = new Roo.View({
3641     el : "my-element",
3642     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
3643  
3644     singleSelect: true,
3645     selectedClass: "ydataview-selected",
3646     store: store
3647  });
3648
3649  // listen for node click?
3650  view.on("click", function(vw, index, node, e){
3651  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
3652  });
3653
3654  // load XML data
3655  dataModel.load("foobar.xml");
3656  </code></pre>
3657  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
3658  * <br><br>
3659  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
3660  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
3661  * 
3662  * Note: old style constructor is still suported (container, template, config)
3663  * 
3664  * @constructor
3665  * Create a new View
3666  * @param {Object} config The config object
3667  * 
3668  */
3669 Roo.View = function(config, depreciated_tpl, depreciated_config){
3670     
3671     this.parent = false;
3672     
3673     if (typeof(depreciated_tpl) == 'undefined') {
3674         // new way.. - universal constructor.
3675         Roo.apply(this, config);
3676         this.el  = Roo.get(this.el);
3677     } else {
3678         // old format..
3679         this.el  = Roo.get(config);
3680         this.tpl = depreciated_tpl;
3681         Roo.apply(this, depreciated_config);
3682     }
3683     this.wrapEl  = this.el.wrap().wrap();
3684     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
3685     
3686     
3687     if(typeof(this.tpl) == "string"){
3688         this.tpl = new Roo.Template(this.tpl);
3689     } else {
3690         // support xtype ctors..
3691         this.tpl = new Roo.factory(this.tpl, Roo);
3692     }
3693     
3694     
3695     this.tpl.compile();
3696     
3697     /** @private */
3698     this.addEvents({
3699         /**
3700          * @event beforeclick
3701          * Fires before a click is processed. Returns false to cancel the default action.
3702          * @param {Roo.View} this
3703          * @param {Number} index The index of the target node
3704          * @param {HTMLElement} node The target node
3705          * @param {Roo.EventObject} e The raw event object
3706          */
3707             "beforeclick" : true,
3708         /**
3709          * @event click
3710          * Fires when a template node is clicked.
3711          * @param {Roo.View} this
3712          * @param {Number} index The index of the target node
3713          * @param {HTMLElement} node The target node
3714          * @param {Roo.EventObject} e The raw event object
3715          */
3716             "click" : true,
3717         /**
3718          * @event dblclick
3719          * Fires when a template node is double clicked.
3720          * @param {Roo.View} this
3721          * @param {Number} index The index of the target node
3722          * @param {HTMLElement} node The target node
3723          * @param {Roo.EventObject} e The raw event object
3724          */
3725             "dblclick" : true,
3726         /**
3727          * @event contextmenu
3728          * Fires when a template node is right clicked.
3729          * @param {Roo.View} this
3730          * @param {Number} index The index of the target node
3731          * @param {HTMLElement} node The target node
3732          * @param {Roo.EventObject} e The raw event object
3733          */
3734             "contextmenu" : true,
3735         /**
3736          * @event selectionchange
3737          * Fires when the selected nodes change.
3738          * @param {Roo.View} this
3739          * @param {Array} selections Array of the selected nodes
3740          */
3741             "selectionchange" : true,
3742     
3743         /**
3744          * @event beforeselect
3745          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
3746          * @param {Roo.View} this
3747          * @param {HTMLElement} node The node to be selected
3748          * @param {Array} selections Array of currently selected nodes
3749          */
3750             "beforeselect" : true,
3751         /**
3752          * @event preparedata
3753          * Fires on every row to render, to allow you to change the data.
3754          * @param {Roo.View} this
3755          * @param {Object} data to be rendered (change this)
3756          */
3757           "preparedata" : true
3758           
3759           
3760         });
3761
3762
3763
3764     this.el.on({
3765         "click": this.onClick,
3766         "dblclick": this.onDblClick,
3767         "contextmenu": this.onContextMenu,
3768         scope:this
3769     });
3770
3771     this.selections = [];
3772     this.nodes = [];
3773     this.cmp = new Roo.CompositeElementLite([]);
3774     if(this.store){
3775         this.store = Roo.factory(this.store, Roo.data);
3776         this.setStore(this.store, true);
3777     }
3778     
3779     if ( this.footer && this.footer.xtype) {
3780            
3781          var fctr = this.wrapEl.appendChild(document.createElement("div"));
3782         
3783         this.footer.dataSource = this.store;
3784         this.footer.container = fctr;
3785         this.footer = Roo.factory(this.footer, Roo);
3786         fctr.insertFirst(this.el);
3787         
3788         // this is a bit insane - as the paging toolbar seems to detach the el..
3789 //        dom.parentNode.parentNode.parentNode
3790          // they get detached?
3791     }
3792     
3793     
3794     Roo.View.superclass.constructor.call(this);
3795     
3796     
3797 };
3798
3799 Roo.extend(Roo.View, Roo.util.Observable, {
3800     
3801      /**
3802      * @cfg {Roo.data.Store} store Data store to load data from.
3803      */
3804     store : false,
3805     
3806     /**
3807      * @cfg {String|Roo.Element} el The container element.
3808      */
3809     el : '',
3810     
3811     /**
3812      * @cfg {String|Roo.Template} tpl The template used by this View 
3813      */
3814     tpl : false,
3815     /**
3816      * @cfg {String} dataName the named area of the template to use as the data area
3817      *                          Works with domtemplates roo-name="name"
3818      */
3819     dataName: false,
3820     /**
3821      * @cfg {String} selectedClass The css class to add to selected nodes
3822      */
3823     selectedClass : "x-view-selected",
3824      /**
3825      * @cfg {String} emptyText The empty text to show when nothing is loaded.
3826      */
3827     emptyText : "",
3828     
3829     /**
3830      * @cfg {String} text to display on mask (default Loading)
3831      */
3832     mask : false,
3833     /**
3834      * @cfg {Boolean} multiSelect Allow multiple selection
3835      */
3836     multiSelect : false,
3837     /**
3838      * @cfg {Boolean} singleSelect Allow single selection
3839      */
3840     singleSelect:  false,
3841     
3842     /**
3843      * @cfg {Boolean} toggleSelect - selecting 
3844      */
3845     toggleSelect : false,
3846     
3847     /**
3848      * @cfg {Boolean} tickable - selecting 
3849      */
3850     tickable : false,
3851     
3852     /**
3853      * Returns the element this view is bound to.
3854      * @return {Roo.Element}
3855      */
3856     getEl : function(){
3857         return this.wrapEl;
3858     },
3859     
3860     
3861
3862     /**
3863      * Refreshes the view. - called by datachanged on the store. - do not call directly.
3864      */
3865     refresh : function(){
3866         //Roo.log('refresh');
3867         var t = this.tpl;
3868         
3869         // if we are using something like 'domtemplate', then
3870         // the what gets used is:
3871         // t.applySubtemplate(NAME, data, wrapping data..)
3872         // the outer template then get' applied with
3873         //     the store 'extra data'
3874         // and the body get's added to the
3875         //      roo-name="data" node?
3876         //      <span class='roo-tpl-{name}'></span> ?????
3877         
3878         
3879         
3880         this.clearSelections();
3881         this.el.update("");
3882         var html = [];
3883         var records = this.store.getRange();
3884         if(records.length < 1) {
3885             
3886             // is this valid??  = should it render a template??
3887             
3888             this.el.update(this.emptyText);
3889             return;
3890         }
3891         var el = this.el;
3892         if (this.dataName) {
3893             this.el.update(t.apply(this.store.meta)); //????
3894             el = this.el.child('.roo-tpl-' + this.dataName);
3895         }
3896         
3897         for(var i = 0, len = records.length; i < len; i++){
3898             var data = this.prepareData(records[i].data, i, records[i]);
3899             this.fireEvent("preparedata", this, data, i, records[i]);
3900             
3901             var d = Roo.apply({}, data);
3902             
3903             if(this.tickable){
3904                 Roo.apply(d, {'roo-id' : Roo.id()});
3905                 
3906                 var _this = this;
3907             
3908                 Roo.each(this.parent.item, function(item){
3909                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
3910                         return;
3911                     }
3912                     Roo.apply(d, {'roo-data-checked' : 'checked'});
3913                 });
3914             }
3915             
3916             html[html.length] = Roo.util.Format.trim(
3917                 this.dataName ?
3918                     t.applySubtemplate(this.dataName, d, this.store.meta) :
3919                     t.apply(d)
3920             );
3921         }
3922         
3923         
3924         
3925         el.update(html.join(""));
3926         this.nodes = el.dom.childNodes;
3927         this.updateIndexes(0);
3928     },
3929     
3930
3931     /**
3932      * Function to override to reformat the data that is sent to
3933      * the template for each node.
3934      * DEPRICATED - use the preparedata event handler.
3935      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
3936      * a JSON object for an UpdateManager bound view).
3937      */
3938     prepareData : function(data, index, record)
3939     {
3940         this.fireEvent("preparedata", this, data, index, record);
3941         return data;
3942     },
3943
3944     onUpdate : function(ds, record){
3945         // Roo.log('on update');   
3946         this.clearSelections();
3947         var index = this.store.indexOf(record);
3948         var n = this.nodes[index];
3949         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
3950         n.parentNode.removeChild(n);
3951         this.updateIndexes(index, index);
3952     },
3953
3954     
3955     
3956 // --------- FIXME     
3957     onAdd : function(ds, records, index)
3958     {
3959         //Roo.log(['on Add', ds, records, index] );        
3960         this.clearSelections();
3961         if(this.nodes.length == 0){
3962             this.refresh();
3963             return;
3964         }
3965         var n = this.nodes[index];
3966         for(var i = 0, len = records.length; i < len; i++){
3967             var d = this.prepareData(records[i].data, i, records[i]);
3968             if(n){
3969                 this.tpl.insertBefore(n, d);
3970             }else{
3971                 
3972                 this.tpl.append(this.el, d);
3973             }
3974         }
3975         this.updateIndexes(index);
3976     },
3977
3978     onRemove : function(ds, record, index){
3979        // Roo.log('onRemove');
3980         this.clearSelections();
3981         var el = this.dataName  ?
3982             this.el.child('.roo-tpl-' + this.dataName) :
3983             this.el; 
3984         
3985         el.dom.removeChild(this.nodes[index]);
3986         this.updateIndexes(index);
3987     },
3988
3989     /**
3990      * Refresh an individual node.
3991      * @param {Number} index
3992      */
3993     refreshNode : function(index){
3994         this.onUpdate(this.store, this.store.getAt(index));
3995     },
3996
3997     updateIndexes : function(startIndex, endIndex){
3998         var ns = this.nodes;
3999         startIndex = startIndex || 0;
4000         endIndex = endIndex || ns.length - 1;
4001         for(var i = startIndex; i <= endIndex; i++){
4002             ns[i].nodeIndex = i;
4003         }
4004     },
4005
4006     /**
4007      * Changes the data store this view uses and refresh the view.
4008      * @param {Store} store
4009      */
4010     setStore : function(store, initial){
4011         if(!initial && this.store){
4012             this.store.un("datachanged", this.refresh);
4013             this.store.un("add", this.onAdd);
4014             this.store.un("remove", this.onRemove);
4015             this.store.un("update", this.onUpdate);
4016             this.store.un("clear", this.refresh);
4017             this.store.un("beforeload", this.onBeforeLoad);
4018             this.store.un("load", this.onLoad);
4019             this.store.un("loadexception", this.onLoad);
4020         }
4021         if(store){
4022           
4023             store.on("datachanged", this.refresh, this);
4024             store.on("add", this.onAdd, this);
4025             store.on("remove", this.onRemove, this);
4026             store.on("update", this.onUpdate, this);
4027             store.on("clear", this.refresh, this);
4028             store.on("beforeload", this.onBeforeLoad, this);
4029             store.on("load", this.onLoad, this);
4030             store.on("loadexception", this.onLoad, this);
4031         }
4032         
4033         if(store){
4034             this.refresh();
4035         }
4036     },
4037     /**
4038      * onbeforeLoad - masks the loading area.
4039      *
4040      */
4041     onBeforeLoad : function(store,opts)
4042     {
4043          //Roo.log('onBeforeLoad');   
4044         if (!opts.add) {
4045             this.el.update("");
4046         }
4047         this.el.mask(this.mask ? this.mask : "Loading" ); 
4048     },
4049     onLoad : function ()
4050     {
4051         this.el.unmask();
4052     },
4053     
4054
4055     /**
4056      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4057      * @param {HTMLElement} node
4058      * @return {HTMLElement} The template node
4059      */
4060     findItemFromChild : function(node){
4061         var el = this.dataName  ?
4062             this.el.child('.roo-tpl-' + this.dataName,true) :
4063             this.el.dom; 
4064         
4065         if(!node || node.parentNode == el){
4066                     return node;
4067             }
4068             var p = node.parentNode;
4069             while(p && p != el){
4070             if(p.parentNode == el){
4071                 return p;
4072             }
4073             p = p.parentNode;
4074         }
4075             return null;
4076     },
4077
4078     /** @ignore */
4079     onClick : function(e){
4080         var item = this.findItemFromChild(e.getTarget());
4081         if(item){
4082             var index = this.indexOf(item);
4083             if(this.onItemClick(item, index, e) !== false){
4084                 this.fireEvent("click", this, index, item, e);
4085             }
4086         }else{
4087             this.clearSelections();
4088         }
4089     },
4090
4091     /** @ignore */
4092     onContextMenu : function(e){
4093         var item = this.findItemFromChild(e.getTarget());
4094         if(item){
4095             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4096         }
4097     },
4098
4099     /** @ignore */
4100     onDblClick : function(e){
4101         var item = this.findItemFromChild(e.getTarget());
4102         if(item){
4103             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4104         }
4105     },
4106
4107     onItemClick : function(item, index, e)
4108     {
4109         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4110             return false;
4111         }
4112         if (this.toggleSelect) {
4113             var m = this.isSelected(item) ? 'unselect' : 'select';
4114             //Roo.log(m);
4115             var _t = this;
4116             _t[m](item, true, false);
4117             return true;
4118         }
4119         if(this.multiSelect || this.singleSelect){
4120             if(this.multiSelect && e.shiftKey && this.lastSelection){
4121                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4122             }else{
4123                 this.select(item, this.multiSelect && e.ctrlKey);
4124                 this.lastSelection = item;
4125             }
4126             
4127             if(!this.tickable){
4128                 e.preventDefault();
4129             }
4130             
4131         }
4132         return true;
4133     },
4134
4135     /**
4136      * Get the number of selected nodes.
4137      * @return {Number}
4138      */
4139     getSelectionCount : function(){
4140         return this.selections.length;
4141     },
4142
4143     /**
4144      * Get the currently selected nodes.
4145      * @return {Array} An array of HTMLElements
4146      */
4147     getSelectedNodes : function(){
4148         return this.selections;
4149     },
4150
4151     /**
4152      * Get the indexes of the selected nodes.
4153      * @return {Array}
4154      */
4155     getSelectedIndexes : function(){
4156         var indexes = [], s = this.selections;
4157         for(var i = 0, len = s.length; i < len; i++){
4158             indexes.push(s[i].nodeIndex);
4159         }
4160         return indexes;
4161     },
4162
4163     /**
4164      * Clear all selections
4165      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4166      */
4167     clearSelections : function(suppressEvent){
4168         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4169             this.cmp.elements = this.selections;
4170             this.cmp.removeClass(this.selectedClass);
4171             this.selections = [];
4172             if(!suppressEvent){
4173                 this.fireEvent("selectionchange", this, this.selections);
4174             }
4175         }
4176     },
4177
4178     /**
4179      * Returns true if the passed node is selected
4180      * @param {HTMLElement/Number} node The node or node index
4181      * @return {Boolean}
4182      */
4183     isSelected : function(node){
4184         var s = this.selections;
4185         if(s.length < 1){
4186             return false;
4187         }
4188         node = this.getNode(node);
4189         return s.indexOf(node) !== -1;
4190     },
4191
4192     /**
4193      * Selects nodes.
4194      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
4195      * @param {Boolean} keepExisting (optional) true to keep existing selections
4196      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4197      */
4198     select : function(nodeInfo, keepExisting, suppressEvent){
4199         if(nodeInfo instanceof Array){
4200             if(!keepExisting){
4201                 this.clearSelections(true);
4202             }
4203             for(var i = 0, len = nodeInfo.length; i < len; i++){
4204                 this.select(nodeInfo[i], true, true);
4205             }
4206             return;
4207         } 
4208         var node = this.getNode(nodeInfo);
4209         if(!node || this.isSelected(node)){
4210             return; // already selected.
4211         }
4212         if(!keepExisting){
4213             this.clearSelections(true);
4214         }
4215         
4216         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4217             Roo.fly(node).addClass(this.selectedClass);
4218             this.selections.push(node);
4219             if(!suppressEvent){
4220                 this.fireEvent("selectionchange", this, this.selections);
4221             }
4222         }
4223         
4224         
4225     },
4226       /**
4227      * Unselects nodes.
4228      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
4229      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4230      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4231      */
4232     unselect : function(nodeInfo, keepExisting, suppressEvent)
4233     {
4234         if(nodeInfo instanceof Array){
4235             Roo.each(this.selections, function(s) {
4236                 this.unselect(s, nodeInfo);
4237             }, this);
4238             return;
4239         }
4240         var node = this.getNode(nodeInfo);
4241         if(!node || !this.isSelected(node)){
4242             //Roo.log("not selected");
4243             return; // not selected.
4244         }
4245         // fireevent???
4246         var ns = [];
4247         Roo.each(this.selections, function(s) {
4248             if (s == node ) {
4249                 Roo.fly(node).removeClass(this.selectedClass);
4250
4251                 return;
4252             }
4253             ns.push(s);
4254         },this);
4255         
4256         this.selections= ns;
4257         this.fireEvent("selectionchange", this, this.selections);
4258     },
4259
4260     /**
4261      * Gets a template node.
4262      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4263      * @return {HTMLElement} The node or null if it wasn't found
4264      */
4265     getNode : function(nodeInfo){
4266         if(typeof nodeInfo == "string"){
4267             return document.getElementById(nodeInfo);
4268         }else if(typeof nodeInfo == "number"){
4269             return this.nodes[nodeInfo];
4270         }
4271         return nodeInfo;
4272     },
4273
4274     /**
4275      * Gets a range template nodes.
4276      * @param {Number} startIndex
4277      * @param {Number} endIndex
4278      * @return {Array} An array of nodes
4279      */
4280     getNodes : function(start, end){
4281         var ns = this.nodes;
4282         start = start || 0;
4283         end = typeof end == "undefined" ? ns.length - 1 : end;
4284         var nodes = [];
4285         if(start <= end){
4286             for(var i = start; i <= end; i++){
4287                 nodes.push(ns[i]);
4288             }
4289         } else{
4290             for(var i = start; i >= end; i--){
4291                 nodes.push(ns[i]);
4292             }
4293         }
4294         return nodes;
4295     },
4296
4297     /**
4298      * Finds the index of the passed node
4299      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4300      * @return {Number} The index of the node or -1
4301      */
4302     indexOf : function(node){
4303         node = this.getNode(node);
4304         if(typeof node.nodeIndex == "number"){
4305             return node.nodeIndex;
4306         }
4307         var ns = this.nodes;
4308         for(var i = 0, len = ns.length; i < len; i++){
4309             if(ns[i] == node){
4310                 return i;
4311             }
4312         }
4313         return -1;
4314     }
4315 });
4316 /*
4317  * Based on:
4318  * Ext JS Library 1.1.1
4319  * Copyright(c) 2006-2007, Ext JS, LLC.
4320  *
4321  * Originally Released Under LGPL - original licence link has changed is not relivant.
4322  *
4323  * Fork - LGPL
4324  * <script type="text/javascript">
4325  */
4326
4327 /**
4328  * @class Roo.JsonView
4329  * @extends Roo.View
4330  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4331 <pre><code>
4332 var view = new Roo.JsonView({
4333     container: "my-element",
4334     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4335     multiSelect: true, 
4336     jsonRoot: "data" 
4337 });
4338
4339 // listen for node click?
4340 view.on("click", function(vw, index, node, e){
4341     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4342 });
4343
4344 // direct load of JSON data
4345 view.load("foobar.php");
4346
4347 // Example from my blog list
4348 var tpl = new Roo.Template(
4349     '&lt;div class="entry"&gt;' +
4350     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4351     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4352     "&lt;/div&gt;&lt;hr /&gt;"
4353 );
4354
4355 var moreView = new Roo.JsonView({
4356     container :  "entry-list", 
4357     template : tpl,
4358     jsonRoot: "posts"
4359 });
4360 moreView.on("beforerender", this.sortEntries, this);
4361 moreView.load({
4362     url: "/blog/get-posts.php",
4363     params: "allposts=true",
4364     text: "Loading Blog Entries..."
4365 });
4366 </code></pre>
4367
4368 * Note: old code is supported with arguments : (container, template, config)
4369
4370
4371  * @constructor
4372  * Create a new JsonView
4373  * 
4374  * @param {Object} config The config object
4375  * 
4376  */
4377 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4378     
4379     
4380     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4381
4382     var um = this.el.getUpdateManager();
4383     um.setRenderer(this);
4384     um.on("update", this.onLoad, this);
4385     um.on("failure", this.onLoadException, this);
4386
4387     /**
4388      * @event beforerender
4389      * Fires before rendering of the downloaded JSON data.
4390      * @param {Roo.JsonView} this
4391      * @param {Object} data The JSON data loaded
4392      */
4393     /**
4394      * @event load
4395      * Fires when data is loaded.
4396      * @param {Roo.JsonView} this
4397      * @param {Object} data The JSON data loaded
4398      * @param {Object} response The raw Connect response object
4399      */
4400     /**
4401      * @event loadexception
4402      * Fires when loading fails.
4403      * @param {Roo.JsonView} this
4404      * @param {Object} response The raw Connect response object
4405      */
4406     this.addEvents({
4407         'beforerender' : true,
4408         'load' : true,
4409         'loadexception' : true
4410     });
4411 };
4412 Roo.extend(Roo.JsonView, Roo.View, {
4413     /**
4414      * @type {String} The root property in the loaded JSON object that contains the data
4415      */
4416     jsonRoot : "",
4417
4418     /**
4419      * Refreshes the view.
4420      */
4421     refresh : function(){
4422         this.clearSelections();
4423         this.el.update("");
4424         var html = [];
4425         var o = this.jsonData;
4426         if(o && o.length > 0){
4427             for(var i = 0, len = o.length; i < len; i++){
4428                 var data = this.prepareData(o[i], i, o);
4429                 html[html.length] = this.tpl.apply(data);
4430             }
4431         }else{
4432             html.push(this.emptyText);
4433         }
4434         this.el.update(html.join(""));
4435         this.nodes = this.el.dom.childNodes;
4436         this.updateIndexes(0);
4437     },
4438
4439     /**
4440      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
4441      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
4442      <pre><code>
4443      view.load({
4444          url: "your-url.php",
4445          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4446          callback: yourFunction,
4447          scope: yourObject, //(optional scope)
4448          discardUrl: false,
4449          nocache: false,
4450          text: "Loading...",
4451          timeout: 30,
4452          scripts: false
4453      });
4454      </code></pre>
4455      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4456      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
4457      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
4458      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4459      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
4460      */
4461     load : function(){
4462         var um = this.el.getUpdateManager();
4463         um.update.apply(um, arguments);
4464     },
4465
4466     // note - render is a standard framework call...
4467     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4468     render : function(el, response){
4469         
4470         this.clearSelections();
4471         this.el.update("");
4472         var o;
4473         try{
4474             if (response != '') {
4475                 o = Roo.util.JSON.decode(response.responseText);
4476                 if(this.jsonRoot){
4477                     
4478                     o = o[this.jsonRoot];
4479                 }
4480             }
4481         } catch(e){
4482         }
4483         /**
4484          * The current JSON data or null
4485          */
4486         this.jsonData = o;
4487         this.beforeRender();
4488         this.refresh();
4489     },
4490
4491 /**
4492  * Get the number of records in the current JSON dataset
4493  * @return {Number}
4494  */
4495     getCount : function(){
4496         return this.jsonData ? this.jsonData.length : 0;
4497     },
4498
4499 /**
4500  * Returns the JSON object for the specified node(s)
4501  * @param {HTMLElement/Array} node The node or an array of nodes
4502  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4503  * you get the JSON object for the node
4504  */
4505     getNodeData : function(node){
4506         if(node instanceof Array){
4507             var data = [];
4508             for(var i = 0, len = node.length; i < len; i++){
4509                 data.push(this.getNodeData(node[i]));
4510             }
4511             return data;
4512         }
4513         return this.jsonData[this.indexOf(node)] || null;
4514     },
4515
4516     beforeRender : function(){
4517         this.snapshot = this.jsonData;
4518         if(this.sortInfo){
4519             this.sort.apply(this, this.sortInfo);
4520         }
4521         this.fireEvent("beforerender", this, this.jsonData);
4522     },
4523
4524     onLoad : function(el, o){
4525         this.fireEvent("load", this, this.jsonData, o);
4526     },
4527
4528     onLoadException : function(el, o){
4529         this.fireEvent("loadexception", this, o);
4530     },
4531
4532 /**
4533  * Filter the data by a specific property.
4534  * @param {String} property A property on your JSON objects
4535  * @param {String/RegExp} value Either string that the property values
4536  * should start with, or a RegExp to test against the property
4537  */
4538     filter : function(property, value){
4539         if(this.jsonData){
4540             var data = [];
4541             var ss = this.snapshot;
4542             if(typeof value == "string"){
4543                 var vlen = value.length;
4544                 if(vlen == 0){
4545                     this.clearFilter();
4546                     return;
4547                 }
4548                 value = value.toLowerCase();
4549                 for(var i = 0, len = ss.length; i < len; i++){
4550                     var o = ss[i];
4551                     if(o[property].substr(0, vlen).toLowerCase() == value){
4552                         data.push(o);
4553                     }
4554                 }
4555             } else if(value.exec){ // regex?
4556                 for(var i = 0, len = ss.length; i < len; i++){
4557                     var o = ss[i];
4558                     if(value.test(o[property])){
4559                         data.push(o);
4560                     }
4561                 }
4562             } else{
4563                 return;
4564             }
4565             this.jsonData = data;
4566             this.refresh();
4567         }
4568     },
4569
4570 /**
4571  * Filter by a function. The passed function will be called with each
4572  * object in the current dataset. If the function returns true the value is kept,
4573  * otherwise it is filtered.
4574  * @param {Function} fn
4575  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4576  */
4577     filterBy : function(fn, scope){
4578         if(this.jsonData){
4579             var data = [];
4580             var ss = this.snapshot;
4581             for(var i = 0, len = ss.length; i < len; i++){
4582                 var o = ss[i];
4583                 if(fn.call(scope || this, o)){
4584                     data.push(o);
4585                 }
4586             }
4587             this.jsonData = data;
4588             this.refresh();
4589         }
4590     },
4591
4592 /**
4593  * Clears the current filter.
4594  */
4595     clearFilter : function(){
4596         if(this.snapshot && this.jsonData != this.snapshot){
4597             this.jsonData = this.snapshot;
4598             this.refresh();
4599         }
4600     },
4601
4602
4603 /**
4604  * Sorts the data for this view and refreshes it.
4605  * @param {String} property A property on your JSON objects to sort on
4606  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
4607  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
4608  */
4609     sort : function(property, dir, sortType){
4610         this.sortInfo = Array.prototype.slice.call(arguments, 0);
4611         if(this.jsonData){
4612             var p = property;
4613             var dsc = dir && dir.toLowerCase() == "desc";
4614             var f = function(o1, o2){
4615                 var v1 = sortType ? sortType(o1[p]) : o1[p];
4616                 var v2 = sortType ? sortType(o2[p]) : o2[p];
4617                 ;
4618                 if(v1 < v2){
4619                     return dsc ? +1 : -1;
4620                 } else if(v1 > v2){
4621                     return dsc ? -1 : +1;
4622                 } else{
4623                     return 0;
4624                 }
4625             };
4626             this.jsonData.sort(f);
4627             this.refresh();
4628             if(this.jsonData != this.snapshot){
4629                 this.snapshot.sort(f);
4630             }
4631         }
4632     }
4633 });/*
4634  * Based on:
4635  * Ext JS Library 1.1.1
4636  * Copyright(c) 2006-2007, Ext JS, LLC.
4637  *
4638  * Originally Released Under LGPL - original licence link has changed is not relivant.
4639  *
4640  * Fork - LGPL
4641  * <script type="text/javascript">
4642  */
4643  
4644
4645 /**
4646  * @class Roo.ColorPalette
4647  * @extends Roo.Component
4648  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
4649  * Here's an example of typical usage:
4650  * <pre><code>
4651 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
4652 cp.render('my-div');
4653
4654 cp.on('select', function(palette, selColor){
4655     // do something with selColor
4656 });
4657 </code></pre>
4658  * @constructor
4659  * Create a new ColorPalette
4660  * @param {Object} config The config object
4661  */
4662 Roo.ColorPalette = function(config){
4663     Roo.ColorPalette.superclass.constructor.call(this, config);
4664     this.addEvents({
4665         /**
4666              * @event select
4667              * Fires when a color is selected
4668              * @param {ColorPalette} this
4669              * @param {String} color The 6-digit color hex code (without the # symbol)
4670              */
4671         select: true
4672     });
4673
4674     if(this.handler){
4675         this.on("select", this.handler, this.scope, true);
4676     }
4677 };
4678 Roo.extend(Roo.ColorPalette, Roo.Component, {
4679     /**
4680      * @cfg {String} itemCls
4681      * The CSS class to apply to the containing element (defaults to "x-color-palette")
4682      */
4683     itemCls : "x-color-palette",
4684     /**
4685      * @cfg {String} value
4686      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
4687      * the hex codes are case-sensitive.
4688      */
4689     value : null,
4690     clickEvent:'click',
4691     // private
4692     ctype: "Roo.ColorPalette",
4693
4694     /**
4695      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
4696      */
4697     allowReselect : false,
4698
4699     /**
4700      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
4701      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
4702      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
4703      * of colors with the width setting until the box is symmetrical.</p>
4704      * <p>You can override individual colors if needed:</p>
4705      * <pre><code>
4706 var cp = new Roo.ColorPalette();
4707 cp.colors[0] = "FF0000";  // change the first box to red
4708 </code></pre>
4709
4710 Or you can provide a custom array of your own for complete control:
4711 <pre><code>
4712 var cp = new Roo.ColorPalette();
4713 cp.colors = ["000000", "993300", "333300"];
4714 </code></pre>
4715      * @type Array
4716      */
4717     colors : [
4718         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
4719         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
4720         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
4721         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
4722         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
4723     ],
4724
4725     // private
4726     onRender : function(container, position){
4727         var t = new Roo.MasterTemplate(
4728             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
4729         );
4730         var c = this.colors;
4731         for(var i = 0, len = c.length; i < len; i++){
4732             t.add([c[i]]);
4733         }
4734         var el = document.createElement("div");
4735         el.className = this.itemCls;
4736         t.overwrite(el);
4737         container.dom.insertBefore(el, position);
4738         this.el = Roo.get(el);
4739         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
4740         if(this.clickEvent != 'click'){
4741             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
4742         }
4743     },
4744
4745     // private
4746     afterRender : function(){
4747         Roo.ColorPalette.superclass.afterRender.call(this);
4748         if(this.value){
4749             var s = this.value;
4750             this.value = null;
4751             this.select(s);
4752         }
4753     },
4754
4755     // private
4756     handleClick : function(e, t){
4757         e.preventDefault();
4758         if(!this.disabled){
4759             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
4760             this.select(c.toUpperCase());
4761         }
4762     },
4763
4764     /**
4765      * Selects the specified color in the palette (fires the select event)
4766      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
4767      */
4768     select : function(color){
4769         color = color.replace("#", "");
4770         if(color != this.value || this.allowReselect){
4771             var el = this.el;
4772             if(this.value){
4773                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
4774             }
4775             el.child("a.color-"+color).addClass("x-color-palette-sel");
4776             this.value = color;
4777             this.fireEvent("select", this, color);
4778         }
4779     }
4780 });/*
4781  * Based on:
4782  * Ext JS Library 1.1.1
4783  * Copyright(c) 2006-2007, Ext JS, LLC.
4784  *
4785  * Originally Released Under LGPL - original licence link has changed is not relivant.
4786  *
4787  * Fork - LGPL
4788  * <script type="text/javascript">
4789  */
4790  
4791 /**
4792  * @class Roo.DatePicker
4793  * @extends Roo.Component
4794  * Simple date picker class.
4795  * @constructor
4796  * Create a new DatePicker
4797  * @param {Object} config The config object
4798  */
4799 Roo.DatePicker = function(config){
4800     Roo.DatePicker.superclass.constructor.call(this, config);
4801
4802     this.value = config && config.value ?
4803                  config.value.clearTime() : new Date().clearTime();
4804
4805     this.addEvents({
4806         /**
4807              * @event select
4808              * Fires when a date is selected
4809              * @param {DatePicker} this
4810              * @param {Date} date The selected date
4811              */
4812         'select': true,
4813         /**
4814              * @event monthchange
4815              * Fires when the displayed month changes 
4816              * @param {DatePicker} this
4817              * @param {Date} date The selected month
4818              */
4819         'monthchange': true
4820     });
4821
4822     if(this.handler){
4823         this.on("select", this.handler,  this.scope || this);
4824     }
4825     // build the disabledDatesRE
4826     if(!this.disabledDatesRE && this.disabledDates){
4827         var dd = this.disabledDates;
4828         var re = "(?:";
4829         for(var i = 0; i < dd.length; i++){
4830             re += dd[i];
4831             if(i != dd.length-1) {
4832                 re += "|";
4833             }
4834         }
4835         this.disabledDatesRE = new RegExp(re + ")");
4836     }
4837 };
4838
4839 Roo.extend(Roo.DatePicker, Roo.Component, {
4840     /**
4841      * @cfg {String} todayText
4842      * The text to display on the button that selects the current date (defaults to "Today")
4843      */
4844     todayText : "Today",
4845     /**
4846      * @cfg {String} okText
4847      * The text to display on the ok button
4848      */
4849     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
4850     /**
4851      * @cfg {String} cancelText
4852      * The text to display on the cancel button
4853      */
4854     cancelText : "Cancel",
4855     /**
4856      * @cfg {String} todayTip
4857      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
4858      */
4859     todayTip : "{0} (Spacebar)",
4860     /**
4861      * @cfg {Date} minDate
4862      * Minimum allowable date (JavaScript date object, defaults to null)
4863      */
4864     minDate : null,
4865     /**
4866      * @cfg {Date} maxDate
4867      * Maximum allowable date (JavaScript date object, defaults to null)
4868      */
4869     maxDate : null,
4870     /**
4871      * @cfg {String} minText
4872      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
4873      */
4874     minText : "This date is before the minimum date",
4875     /**
4876      * @cfg {String} maxText
4877      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
4878      */
4879     maxText : "This date is after the maximum date",
4880     /**
4881      * @cfg {String} format
4882      * The default date format string which can be overriden for localization support.  The format must be
4883      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
4884      */
4885     format : "m/d/y",
4886     /**
4887      * @cfg {Array} disabledDays
4888      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
4889      */
4890     disabledDays : null,
4891     /**
4892      * @cfg {String} disabledDaysText
4893      * The tooltip to display when the date falls on a disabled day (defaults to "")
4894      */
4895     disabledDaysText : "",
4896     /**
4897      * @cfg {RegExp} disabledDatesRE
4898      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
4899      */
4900     disabledDatesRE : null,
4901     /**
4902      * @cfg {String} disabledDatesText
4903      * The tooltip text to display when the date falls on a disabled date (defaults to "")
4904      */
4905     disabledDatesText : "",
4906     /**
4907      * @cfg {Boolean} constrainToViewport
4908      * True to constrain the date picker to the viewport (defaults to true)
4909      */
4910     constrainToViewport : true,
4911     /**
4912      * @cfg {Array} monthNames
4913      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
4914      */
4915     monthNames : Date.monthNames,
4916     /**
4917      * @cfg {Array} dayNames
4918      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
4919      */
4920     dayNames : Date.dayNames,
4921     /**
4922      * @cfg {String} nextText
4923      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
4924      */
4925     nextText: 'Next Month (Control+Right)',
4926     /**
4927      * @cfg {String} prevText
4928      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
4929      */
4930     prevText: 'Previous Month (Control+Left)',
4931     /**
4932      * @cfg {String} monthYearText
4933      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
4934      */
4935     monthYearText: 'Choose a month (Control+Up/Down to move years)',
4936     /**
4937      * @cfg {Number} startDay
4938      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
4939      */
4940     startDay : 0,
4941     /**
4942      * @cfg {Bool} showClear
4943      * Show a clear button (usefull for date form elements that can be blank.)
4944      */
4945     
4946     showClear: false,
4947     
4948     /**
4949      * Sets the value of the date field
4950      * @param {Date} value The date to set
4951      */
4952     setValue : function(value){
4953         var old = this.value;
4954         
4955         if (typeof(value) == 'string') {
4956          
4957             value = Date.parseDate(value, this.format);
4958         }
4959         if (!value) {
4960             value = new Date();
4961         }
4962         
4963         this.value = value.clearTime(true);
4964         if(this.el){
4965             this.update(this.value);
4966         }
4967     },
4968
4969     /**
4970      * Gets the current selected value of the date field
4971      * @return {Date} The selected date
4972      */
4973     getValue : function(){
4974         return this.value;
4975     },
4976
4977     // private
4978     focus : function(){
4979         if(this.el){
4980             this.update(this.activeDate);
4981         }
4982     },
4983
4984     // privateval
4985     onRender : function(container, position){
4986         
4987         var m = [
4988              '<table cellspacing="0">',
4989                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
4990                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
4991         var dn = this.dayNames;
4992         for(var i = 0; i < 7; i++){
4993             var d = this.startDay+i;
4994             if(d > 6){
4995                 d = d-7;
4996             }
4997             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
4998         }
4999         m[m.length] = "</tr></thead><tbody><tr>";
5000         for(var i = 0; i < 42; i++) {
5001             if(i % 7 == 0 && i != 0){
5002                 m[m.length] = "</tr><tr>";
5003             }
5004             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5005         }
5006         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5007             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5008
5009         var el = document.createElement("div");
5010         el.className = "x-date-picker";
5011         el.innerHTML = m.join("");
5012
5013         container.dom.insertBefore(el, position);
5014
5015         this.el = Roo.get(el);
5016         this.eventEl = Roo.get(el.firstChild);
5017
5018         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5019             handler: this.showPrevMonth,
5020             scope: this,
5021             preventDefault:true,
5022             stopDefault:true
5023         });
5024
5025         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5026             handler: this.showNextMonth,
5027             scope: this,
5028             preventDefault:true,
5029             stopDefault:true
5030         });
5031
5032         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5033
5034         this.monthPicker = this.el.down('div.x-date-mp');
5035         this.monthPicker.enableDisplayMode('block');
5036         
5037         var kn = new Roo.KeyNav(this.eventEl, {
5038             "left" : function(e){
5039                 e.ctrlKey ?
5040                     this.showPrevMonth() :
5041                     this.update(this.activeDate.add("d", -1));
5042             },
5043
5044             "right" : function(e){
5045                 e.ctrlKey ?
5046                     this.showNextMonth() :
5047                     this.update(this.activeDate.add("d", 1));
5048             },
5049
5050             "up" : function(e){
5051                 e.ctrlKey ?
5052                     this.showNextYear() :
5053                     this.update(this.activeDate.add("d", -7));
5054             },
5055
5056             "down" : function(e){
5057                 e.ctrlKey ?
5058                     this.showPrevYear() :
5059                     this.update(this.activeDate.add("d", 7));
5060             },
5061
5062             "pageUp" : function(e){
5063                 this.showNextMonth();
5064             },
5065
5066             "pageDown" : function(e){
5067                 this.showPrevMonth();
5068             },
5069
5070             "enter" : function(e){
5071                 e.stopPropagation();
5072                 return true;
5073             },
5074
5075             scope : this
5076         });
5077
5078         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5079
5080         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5081
5082         this.el.unselectable();
5083         
5084         this.cells = this.el.select("table.x-date-inner tbody td");
5085         this.textNodes = this.el.query("table.x-date-inner tbody span");
5086
5087         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5088             text: "&#160;",
5089             tooltip: this.monthYearText
5090         });
5091
5092         this.mbtn.on('click', this.showMonthPicker, this);
5093         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5094
5095
5096         var today = (new Date()).dateFormat(this.format);
5097         
5098         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5099         if (this.showClear) {
5100             baseTb.add( new Roo.Toolbar.Fill());
5101         }
5102         baseTb.add({
5103             text: String.format(this.todayText, today),
5104             tooltip: String.format(this.todayTip, today),
5105             handler: this.selectToday,
5106             scope: this
5107         });
5108         
5109         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5110             
5111         //});
5112         if (this.showClear) {
5113             
5114             baseTb.add( new Roo.Toolbar.Fill());
5115             baseTb.add({
5116                 text: '&#160;',
5117                 cls: 'x-btn-icon x-btn-clear',
5118                 handler: function() {
5119                     //this.value = '';
5120                     this.fireEvent("select", this, '');
5121                 },
5122                 scope: this
5123             });
5124         }
5125         
5126         
5127         if(Roo.isIE){
5128             this.el.repaint();
5129         }
5130         this.update(this.value);
5131     },
5132
5133     createMonthPicker : function(){
5134         if(!this.monthPicker.dom.firstChild){
5135             var buf = ['<table border="0" cellspacing="0">'];
5136             for(var i = 0; i < 6; i++){
5137                 buf.push(
5138                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5139                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5140                     i == 0 ?
5141                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
5142                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5143                 );
5144             }
5145             buf.push(
5146                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5147                     this.okText,
5148                     '</button><button type="button" class="x-date-mp-cancel">',
5149                     this.cancelText,
5150                     '</button></td></tr>',
5151                 '</table>'
5152             );
5153             this.monthPicker.update(buf.join(''));
5154             this.monthPicker.on('click', this.onMonthClick, this);
5155             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5156
5157             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5158             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5159
5160             this.mpMonths.each(function(m, a, i){
5161                 i += 1;
5162                 if((i%2) == 0){
5163                     m.dom.xmonth = 5 + Math.round(i * .5);
5164                 }else{
5165                     m.dom.xmonth = Math.round((i-1) * .5);
5166                 }
5167             });
5168         }
5169     },
5170
5171     showMonthPicker : function(){
5172         this.createMonthPicker();
5173         var size = this.el.getSize();
5174         this.monthPicker.setSize(size);
5175         this.monthPicker.child('table').setSize(size);
5176
5177         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5178         this.updateMPMonth(this.mpSelMonth);
5179         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5180         this.updateMPYear(this.mpSelYear);
5181
5182         this.monthPicker.slideIn('t', {duration:.2});
5183     },
5184
5185     updateMPYear : function(y){
5186         this.mpyear = y;
5187         var ys = this.mpYears.elements;
5188         for(var i = 1; i <= 10; i++){
5189             var td = ys[i-1], y2;
5190             if((i%2) == 0){
5191                 y2 = y + Math.round(i * .5);
5192                 td.firstChild.innerHTML = y2;
5193                 td.xyear = y2;
5194             }else{
5195                 y2 = y - (5-Math.round(i * .5));
5196                 td.firstChild.innerHTML = y2;
5197                 td.xyear = y2;
5198             }
5199             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5200         }
5201     },
5202
5203     updateMPMonth : function(sm){
5204         this.mpMonths.each(function(m, a, i){
5205             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5206         });
5207     },
5208
5209     selectMPMonth: function(m){
5210         
5211     },
5212
5213     onMonthClick : function(e, t){
5214         e.stopEvent();
5215         var el = new Roo.Element(t), pn;
5216         if(el.is('button.x-date-mp-cancel')){
5217             this.hideMonthPicker();
5218         }
5219         else if(el.is('button.x-date-mp-ok')){
5220             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5221             this.hideMonthPicker();
5222         }
5223         else if(pn = el.up('td.x-date-mp-month', 2)){
5224             this.mpMonths.removeClass('x-date-mp-sel');
5225             pn.addClass('x-date-mp-sel');
5226             this.mpSelMonth = pn.dom.xmonth;
5227         }
5228         else if(pn = el.up('td.x-date-mp-year', 2)){
5229             this.mpYears.removeClass('x-date-mp-sel');
5230             pn.addClass('x-date-mp-sel');
5231             this.mpSelYear = pn.dom.xyear;
5232         }
5233         else if(el.is('a.x-date-mp-prev')){
5234             this.updateMPYear(this.mpyear-10);
5235         }
5236         else if(el.is('a.x-date-mp-next')){
5237             this.updateMPYear(this.mpyear+10);
5238         }
5239     },
5240
5241     onMonthDblClick : function(e, t){
5242         e.stopEvent();
5243         var el = new Roo.Element(t), pn;
5244         if(pn = el.up('td.x-date-mp-month', 2)){
5245             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5246             this.hideMonthPicker();
5247         }
5248         else if(pn = el.up('td.x-date-mp-year', 2)){
5249             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5250             this.hideMonthPicker();
5251         }
5252     },
5253
5254     hideMonthPicker : function(disableAnim){
5255         if(this.monthPicker){
5256             if(disableAnim === true){
5257                 this.monthPicker.hide();
5258             }else{
5259                 this.monthPicker.slideOut('t', {duration:.2});
5260             }
5261         }
5262     },
5263
5264     // private
5265     showPrevMonth : function(e){
5266         this.update(this.activeDate.add("mo", -1));
5267     },
5268
5269     // private
5270     showNextMonth : function(e){
5271         this.update(this.activeDate.add("mo", 1));
5272     },
5273
5274     // private
5275     showPrevYear : function(){
5276         this.update(this.activeDate.add("y", -1));
5277     },
5278
5279     // private
5280     showNextYear : function(){
5281         this.update(this.activeDate.add("y", 1));
5282     },
5283
5284     // private
5285     handleMouseWheel : function(e){
5286         var delta = e.getWheelDelta();
5287         if(delta > 0){
5288             this.showPrevMonth();
5289             e.stopEvent();
5290         } else if(delta < 0){
5291             this.showNextMonth();
5292             e.stopEvent();
5293         }
5294     },
5295
5296     // private
5297     handleDateClick : function(e, t){
5298         e.stopEvent();
5299         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5300             this.setValue(new Date(t.dateValue));
5301             this.fireEvent("select", this, this.value);
5302         }
5303     },
5304
5305     // private
5306     selectToday : function(){
5307         this.setValue(new Date().clearTime());
5308         this.fireEvent("select", this, this.value);
5309     },
5310
5311     // private
5312     update : function(date)
5313     {
5314         var vd = this.activeDate;
5315         this.activeDate = date;
5316         if(vd && this.el){
5317             var t = date.getTime();
5318             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5319                 this.cells.removeClass("x-date-selected");
5320                 this.cells.each(function(c){
5321                    if(c.dom.firstChild.dateValue == t){
5322                        c.addClass("x-date-selected");
5323                        setTimeout(function(){
5324                             try{c.dom.firstChild.focus();}catch(e){}
5325                        }, 50);
5326                        return false;
5327                    }
5328                 });
5329                 return;
5330             }
5331         }
5332         
5333         var days = date.getDaysInMonth();
5334         var firstOfMonth = date.getFirstDateOfMonth();
5335         var startingPos = firstOfMonth.getDay()-this.startDay;
5336
5337         if(startingPos <= this.startDay){
5338             startingPos += 7;
5339         }
5340
5341         var pm = date.add("mo", -1);
5342         var prevStart = pm.getDaysInMonth()-startingPos;
5343
5344         var cells = this.cells.elements;
5345         var textEls = this.textNodes;
5346         days += startingPos;
5347
5348         // convert everything to numbers so it's fast
5349         var day = 86400000;
5350         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5351         var today = new Date().clearTime().getTime();
5352         var sel = date.clearTime().getTime();
5353         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5354         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5355         var ddMatch = this.disabledDatesRE;
5356         var ddText = this.disabledDatesText;
5357         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5358         var ddaysText = this.disabledDaysText;
5359         var format = this.format;
5360
5361         var setCellClass = function(cal, cell){
5362             cell.title = "";
5363             var t = d.getTime();
5364             cell.firstChild.dateValue = t;
5365             if(t == today){
5366                 cell.className += " x-date-today";
5367                 cell.title = cal.todayText;
5368             }
5369             if(t == sel){
5370                 cell.className += " x-date-selected";
5371                 setTimeout(function(){
5372                     try{cell.firstChild.focus();}catch(e){}
5373                 }, 50);
5374             }
5375             // disabling
5376             if(t < min) {
5377                 cell.className = " x-date-disabled";
5378                 cell.title = cal.minText;
5379                 return;
5380             }
5381             if(t > max) {
5382                 cell.className = " x-date-disabled";
5383                 cell.title = cal.maxText;
5384                 return;
5385             }
5386             if(ddays){
5387                 if(ddays.indexOf(d.getDay()) != -1){
5388                     cell.title = ddaysText;
5389                     cell.className = " x-date-disabled";
5390                 }
5391             }
5392             if(ddMatch && format){
5393                 var fvalue = d.dateFormat(format);
5394                 if(ddMatch.test(fvalue)){
5395                     cell.title = ddText.replace("%0", fvalue);
5396                     cell.className = " x-date-disabled";
5397                 }
5398             }
5399         };
5400
5401         var i = 0;
5402         for(; i < startingPos; i++) {
5403             textEls[i].innerHTML = (++prevStart);
5404             d.setDate(d.getDate()+1);
5405             cells[i].className = "x-date-prevday";
5406             setCellClass(this, cells[i]);
5407         }
5408         for(; i < days; i++){
5409             intDay = i - startingPos + 1;
5410             textEls[i].innerHTML = (intDay);
5411             d.setDate(d.getDate()+1);
5412             cells[i].className = "x-date-active";
5413             setCellClass(this, cells[i]);
5414         }
5415         var extraDays = 0;
5416         for(; i < 42; i++) {
5417              textEls[i].innerHTML = (++extraDays);
5418              d.setDate(d.getDate()+1);
5419              cells[i].className = "x-date-nextday";
5420              setCellClass(this, cells[i]);
5421         }
5422
5423         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5424         this.fireEvent('monthchange', this, date);
5425         
5426         if(!this.internalRender){
5427             var main = this.el.dom.firstChild;
5428             var w = main.offsetWidth;
5429             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5430             Roo.fly(main).setWidth(w);
5431             this.internalRender = true;
5432             // opera does not respect the auto grow header center column
5433             // then, after it gets a width opera refuses to recalculate
5434             // without a second pass
5435             if(Roo.isOpera && !this.secondPass){
5436                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5437                 this.secondPass = true;
5438                 this.update.defer(10, this, [date]);
5439             }
5440         }
5441         
5442         
5443     }
5444 });        /*
5445  * Based on:
5446  * Ext JS Library 1.1.1
5447  * Copyright(c) 2006-2007, Ext JS, LLC.
5448  *
5449  * Originally Released Under LGPL - original licence link has changed is not relivant.
5450  *
5451  * Fork - LGPL
5452  * <script type="text/javascript">
5453  */
5454 /**
5455  * @class Roo.TabPanel
5456  * @extends Roo.util.Observable
5457  * A lightweight tab container.
5458  * <br><br>
5459  * Usage:
5460  * <pre><code>
5461 // basic tabs 1, built from existing content
5462 var tabs = new Roo.TabPanel("tabs1");
5463 tabs.addTab("script", "View Script");
5464 tabs.addTab("markup", "View Markup");
5465 tabs.activate("script");
5466
5467 // more advanced tabs, built from javascript
5468 var jtabs = new Roo.TabPanel("jtabs");
5469 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5470
5471 // set up the UpdateManager
5472 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5473 var updater = tab2.getUpdateManager();
5474 updater.setDefaultUrl("ajax1.htm");
5475 tab2.on('activate', updater.refresh, updater, true);
5476
5477 // Use setUrl for Ajax loading
5478 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5479 tab3.setUrl("ajax2.htm", null, true);
5480
5481 // Disabled tab
5482 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5483 tab4.disable();
5484
5485 jtabs.activate("jtabs-1");
5486  * </code></pre>
5487  * @constructor
5488  * Create a new TabPanel.
5489  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5490  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5491  */
5492 Roo.TabPanel = function(container, config){
5493     /**
5494     * The container element for this TabPanel.
5495     * @type Roo.Element
5496     */
5497     this.el = Roo.get(container, true);
5498     if(config){
5499         if(typeof config == "boolean"){
5500             this.tabPosition = config ? "bottom" : "top";
5501         }else{
5502             Roo.apply(this, config);
5503         }
5504     }
5505     if(this.tabPosition == "bottom"){
5506         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5507         this.el.addClass("x-tabs-bottom");
5508     }
5509     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5510     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5511     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5512     if(Roo.isIE){
5513         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5514     }
5515     if(this.tabPosition != "bottom"){
5516         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5517          * @type Roo.Element
5518          */
5519         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5520         this.el.addClass("x-tabs-top");
5521     }
5522     this.items = [];
5523
5524     this.bodyEl.setStyle("position", "relative");
5525
5526     this.active = null;
5527     this.activateDelegate = this.activate.createDelegate(this);
5528
5529     this.addEvents({
5530         /**
5531          * @event tabchange
5532          * Fires when the active tab changes
5533          * @param {Roo.TabPanel} this
5534          * @param {Roo.TabPanelItem} activePanel The new active tab
5535          */
5536         "tabchange": true,
5537         /**
5538          * @event beforetabchange
5539          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5540          * @param {Roo.TabPanel} this
5541          * @param {Object} e Set cancel to true on this object to cancel the tab change
5542          * @param {Roo.TabPanelItem} tab The tab being changed to
5543          */
5544         "beforetabchange" : true
5545     });
5546
5547     Roo.EventManager.onWindowResize(this.onResize, this);
5548     this.cpad = this.el.getPadding("lr");
5549     this.hiddenCount = 0;
5550
5551
5552     // toolbar on the tabbar support...
5553     if (this.toolbar) {
5554         var tcfg = this.toolbar;
5555         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
5556         this.toolbar = new Roo.Toolbar(tcfg);
5557         if (Roo.isSafari) {
5558             var tbl = tcfg.container.child('table', true);
5559             tbl.setAttribute('width', '100%');
5560         }
5561         
5562     }
5563    
5564
5565
5566     Roo.TabPanel.superclass.constructor.call(this);
5567 };
5568
5569 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5570     /*
5571      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5572      */
5573     tabPosition : "top",
5574     /*
5575      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5576      */
5577     currentTabWidth : 0,
5578     /*
5579      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5580      */
5581     minTabWidth : 40,
5582     /*
5583      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5584      */
5585     maxTabWidth : 250,
5586     /*
5587      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5588      */
5589     preferredTabWidth : 175,
5590     /*
5591      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
5592      */
5593     resizeTabs : false,
5594     /*
5595      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
5596      */
5597     monitorResize : true,
5598     /*
5599      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
5600      */
5601     toolbar : false,
5602
5603     /**
5604      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
5605      * @param {String} id The id of the div to use <b>or create</b>
5606      * @param {String} text The text for the tab
5607      * @param {String} content (optional) Content to put in the TabPanelItem body
5608      * @param {Boolean} closable (optional) True to create a close icon on the tab
5609      * @return {Roo.TabPanelItem} The created TabPanelItem
5610      */
5611     addTab : function(id, text, content, closable){
5612         var item = new Roo.TabPanelItem(this, id, text, closable);
5613         this.addTabItem(item);
5614         if(content){
5615             item.setContent(content);
5616         }
5617         return item;
5618     },
5619
5620     /**
5621      * Returns the {@link Roo.TabPanelItem} with the specified id/index
5622      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
5623      * @return {Roo.TabPanelItem}
5624      */
5625     getTab : function(id){
5626         return this.items[id];
5627     },
5628
5629     /**
5630      * Hides the {@link Roo.TabPanelItem} with the specified id/index
5631      * @param {String/Number} id The id or index of the TabPanelItem to hide.
5632      */
5633     hideTab : function(id){
5634         var t = this.items[id];
5635         if(!t.isHidden()){
5636            t.setHidden(true);
5637            this.hiddenCount++;
5638            this.autoSizeTabs();
5639         }
5640     },
5641
5642     /**
5643      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
5644      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
5645      */
5646     unhideTab : function(id){
5647         var t = this.items[id];
5648         if(t.isHidden()){
5649            t.setHidden(false);
5650            this.hiddenCount--;
5651            this.autoSizeTabs();
5652         }
5653     },
5654
5655     /**
5656      * Adds an existing {@link Roo.TabPanelItem}.
5657      * @param {Roo.TabPanelItem} item The TabPanelItem to add
5658      */
5659     addTabItem : function(item){
5660         this.items[item.id] = item;
5661         this.items.push(item);
5662         if(this.resizeTabs){
5663            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
5664            this.autoSizeTabs();
5665         }else{
5666             item.autoSize();
5667         }
5668     },
5669
5670     /**
5671      * Removes a {@link Roo.TabPanelItem}.
5672      * @param {String/Number} id The id or index of the TabPanelItem to remove.
5673      */
5674     removeTab : function(id){
5675         var items = this.items;
5676         var tab = items[id];
5677         if(!tab) { return; }
5678         var index = items.indexOf(tab);
5679         if(this.active == tab && items.length > 1){
5680             var newTab = this.getNextAvailable(index);
5681             if(newTab) {
5682                 newTab.activate();
5683             }
5684         }
5685         this.stripEl.dom.removeChild(tab.pnode.dom);
5686         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
5687             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
5688         }
5689         items.splice(index, 1);
5690         delete this.items[tab.id];
5691         tab.fireEvent("close", tab);
5692         tab.purgeListeners();
5693         this.autoSizeTabs();
5694     },
5695
5696     getNextAvailable : function(start){
5697         var items = this.items;
5698         var index = start;
5699         // look for a next tab that will slide over to
5700         // replace the one being removed
5701         while(index < items.length){
5702             var item = items[++index];
5703             if(item && !item.isHidden()){
5704                 return item;
5705             }
5706         }
5707         // if one isn't found select the previous tab (on the left)
5708         index = start;
5709         while(index >= 0){
5710             var item = items[--index];
5711             if(item && !item.isHidden()){
5712                 return item;
5713             }
5714         }
5715         return null;
5716     },
5717
5718     /**
5719      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
5720      * @param {String/Number} id The id or index of the TabPanelItem to disable.
5721      */
5722     disableTab : function(id){
5723         var tab = this.items[id];
5724         if(tab && this.active != tab){
5725             tab.disable();
5726         }
5727     },
5728
5729     /**
5730      * Enables a {@link Roo.TabPanelItem} that is disabled.
5731      * @param {String/Number} id The id or index of the TabPanelItem to enable.
5732      */
5733     enableTab : function(id){
5734         var tab = this.items[id];
5735         tab.enable();
5736     },
5737
5738     /**
5739      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
5740      * @param {String/Number} id The id or index of the TabPanelItem to activate.
5741      * @return {Roo.TabPanelItem} The TabPanelItem.
5742      */
5743     activate : function(id){
5744         var tab = this.items[id];
5745         if(!tab){
5746             return null;
5747         }
5748         if(tab == this.active || tab.disabled){
5749             return tab;
5750         }
5751         var e = {};
5752         this.fireEvent("beforetabchange", this, e, tab);
5753         if(e.cancel !== true && !tab.disabled){
5754             if(this.active){
5755                 this.active.hide();
5756             }
5757             this.active = this.items[id];
5758             this.active.show();
5759             this.fireEvent("tabchange", this, this.active);
5760         }
5761         return tab;
5762     },
5763
5764     /**
5765      * Gets the active {@link Roo.TabPanelItem}.
5766      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
5767      */
5768     getActiveTab : function(){
5769         return this.active;
5770     },
5771
5772     /**
5773      * Updates the tab body element to fit the height of the container element
5774      * for overflow scrolling
5775      * @param {Number} targetHeight (optional) Override the starting height from the elements height
5776      */
5777     syncHeight : function(targetHeight){
5778         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
5779         var bm = this.bodyEl.getMargins();
5780         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
5781         this.bodyEl.setHeight(newHeight);
5782         return newHeight;
5783     },
5784
5785     onResize : function(){
5786         if(this.monitorResize){
5787             this.autoSizeTabs();
5788         }
5789     },
5790
5791     /**
5792      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
5793      */
5794     beginUpdate : function(){
5795         this.updating = true;
5796     },
5797
5798     /**
5799      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
5800      */
5801     endUpdate : function(){
5802         this.updating = false;
5803         this.autoSizeTabs();
5804     },
5805
5806     /**
5807      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
5808      */
5809     autoSizeTabs : function(){
5810         var count = this.items.length;
5811         var vcount = count - this.hiddenCount;
5812         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
5813             return;
5814         }
5815         var w = Math.max(this.el.getWidth() - this.cpad, 10);
5816         var availWidth = Math.floor(w / vcount);
5817         var b = this.stripBody;
5818         if(b.getWidth() > w){
5819             var tabs = this.items;
5820             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
5821             if(availWidth < this.minTabWidth){
5822                 /*if(!this.sleft){    // incomplete scrolling code
5823                     this.createScrollButtons();
5824                 }
5825                 this.showScroll();
5826                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
5827             }
5828         }else{
5829             if(this.currentTabWidth < this.preferredTabWidth){
5830                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
5831             }
5832         }
5833     },
5834
5835     /**
5836      * Returns the number of tabs in this TabPanel.
5837      * @return {Number}
5838      */
5839      getCount : function(){
5840          return this.items.length;
5841      },
5842
5843     /**
5844      * Resizes all the tabs to the passed width
5845      * @param {Number} The new width
5846      */
5847     setTabWidth : function(width){
5848         this.currentTabWidth = width;
5849         for(var i = 0, len = this.items.length; i < len; i++) {
5850                 if(!this.items[i].isHidden()) {
5851                 this.items[i].setWidth(width);
5852             }
5853         }
5854     },
5855
5856     /**
5857      * Destroys this TabPanel
5858      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
5859      */
5860     destroy : function(removeEl){
5861         Roo.EventManager.removeResizeListener(this.onResize, this);
5862         for(var i = 0, len = this.items.length; i < len; i++){
5863             this.items[i].purgeListeners();
5864         }
5865         if(removeEl === true){
5866             this.el.update("");
5867             this.el.remove();
5868         }
5869     }
5870 });
5871
5872 /**
5873  * @class Roo.TabPanelItem
5874  * @extends Roo.util.Observable
5875  * Represents an individual item (tab plus body) in a TabPanel.
5876  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
5877  * @param {String} id The id of this TabPanelItem
5878  * @param {String} text The text for the tab of this TabPanelItem
5879  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
5880  */
5881 Roo.TabPanelItem = function(tabPanel, id, text, closable){
5882     /**
5883      * The {@link Roo.TabPanel} this TabPanelItem belongs to
5884      * @type Roo.TabPanel
5885      */
5886     this.tabPanel = tabPanel;
5887     /**
5888      * The id for this TabPanelItem
5889      * @type String
5890      */
5891     this.id = id;
5892     /** @private */
5893     this.disabled = false;
5894     /** @private */
5895     this.text = text;
5896     /** @private */
5897     this.loaded = false;
5898     this.closable = closable;
5899
5900     /**
5901      * The body element for this TabPanelItem.
5902      * @type Roo.Element
5903      */
5904     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
5905     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
5906     this.bodyEl.setStyle("display", "block");
5907     this.bodyEl.setStyle("zoom", "1");
5908     this.hideAction();
5909
5910     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
5911     /** @private */
5912     this.el = Roo.get(els.el, true);
5913     this.inner = Roo.get(els.inner, true);
5914     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
5915     this.pnode = Roo.get(els.el.parentNode, true);
5916     this.el.on("mousedown", this.onTabMouseDown, this);
5917     this.el.on("click", this.onTabClick, this);
5918     /** @private */
5919     if(closable){
5920         var c = Roo.get(els.close, true);
5921         c.dom.title = this.closeText;
5922         c.addClassOnOver("close-over");
5923         c.on("click", this.closeClick, this);
5924      }
5925
5926     this.addEvents({
5927          /**
5928          * @event activate
5929          * Fires when this tab becomes the active tab.
5930          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5931          * @param {Roo.TabPanelItem} this
5932          */
5933         "activate": true,
5934         /**
5935          * @event beforeclose
5936          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
5937          * @param {Roo.TabPanelItem} this
5938          * @param {Object} e Set cancel to true on this object to cancel the close.
5939          */
5940         "beforeclose": true,
5941         /**
5942          * @event close
5943          * Fires when this tab is closed.
5944          * @param {Roo.TabPanelItem} this
5945          */
5946          "close": true,
5947         /**
5948          * @event deactivate
5949          * Fires when this tab is no longer the active tab.
5950          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5951          * @param {Roo.TabPanelItem} this
5952          */
5953          "deactivate" : true
5954     });
5955     this.hidden = false;
5956
5957     Roo.TabPanelItem.superclass.constructor.call(this);
5958 };
5959
5960 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
5961     purgeListeners : function(){
5962        Roo.util.Observable.prototype.purgeListeners.call(this);
5963        this.el.removeAllListeners();
5964     },
5965     /**
5966      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
5967      */
5968     show : function(){
5969         this.pnode.addClass("on");
5970         this.showAction();
5971         if(Roo.isOpera){
5972             this.tabPanel.stripWrap.repaint();
5973         }
5974         this.fireEvent("activate", this.tabPanel, this);
5975     },
5976
5977     /**
5978      * Returns true if this tab is the active tab.
5979      * @return {Boolean}
5980      */
5981     isActive : function(){
5982         return this.tabPanel.getActiveTab() == this;
5983     },
5984
5985     /**
5986      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
5987      */
5988     hide : function(){
5989         this.pnode.removeClass("on");
5990         this.hideAction();
5991         this.fireEvent("deactivate", this.tabPanel, this);
5992     },
5993
5994     hideAction : function(){
5995         this.bodyEl.hide();
5996         this.bodyEl.setStyle("position", "absolute");
5997         this.bodyEl.setLeft("-20000px");
5998         this.bodyEl.setTop("-20000px");
5999     },
6000
6001     showAction : function(){
6002         this.bodyEl.setStyle("position", "relative");
6003         this.bodyEl.setTop("");
6004         this.bodyEl.setLeft("");
6005         this.bodyEl.show();
6006     },
6007
6008     /**
6009      * Set the tooltip for the tab.
6010      * @param {String} tooltip The tab's tooltip
6011      */
6012     setTooltip : function(text){
6013         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6014             this.textEl.dom.qtip = text;
6015             this.textEl.dom.removeAttribute('title');
6016         }else{
6017             this.textEl.dom.title = text;
6018         }
6019     },
6020
6021     onTabClick : function(e){
6022         e.preventDefault();
6023         this.tabPanel.activate(this.id);
6024     },
6025
6026     onTabMouseDown : function(e){
6027         e.preventDefault();
6028         this.tabPanel.activate(this.id);
6029     },
6030
6031     getWidth : function(){
6032         return this.inner.getWidth();
6033     },
6034
6035     setWidth : function(width){
6036         var iwidth = width - this.pnode.getPadding("lr");
6037         this.inner.setWidth(iwidth);
6038         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6039         this.pnode.setWidth(width);
6040     },
6041
6042     /**
6043      * Show or hide the tab
6044      * @param {Boolean} hidden True to hide or false to show.
6045      */
6046     setHidden : function(hidden){
6047         this.hidden = hidden;
6048         this.pnode.setStyle("display", hidden ? "none" : "");
6049     },
6050
6051     /**
6052      * Returns true if this tab is "hidden"
6053      * @return {Boolean}
6054      */
6055     isHidden : function(){
6056         return this.hidden;
6057     },
6058
6059     /**
6060      * Returns the text for this tab
6061      * @return {String}
6062      */
6063     getText : function(){
6064         return this.text;
6065     },
6066
6067     autoSize : function(){
6068         //this.el.beginMeasure();
6069         this.textEl.setWidth(1);
6070         /*
6071          *  #2804 [new] Tabs in Roojs
6072          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6073          */
6074         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6075         //this.el.endMeasure();
6076     },
6077
6078     /**
6079      * Sets the text for the tab (Note: this also sets the tooltip text)
6080      * @param {String} text The tab's text and tooltip
6081      */
6082     setText : function(text){
6083         this.text = text;
6084         this.textEl.update(text);
6085         this.setTooltip(text);
6086         if(!this.tabPanel.resizeTabs){
6087             this.autoSize();
6088         }
6089     },
6090     /**
6091      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6092      */
6093     activate : function(){
6094         this.tabPanel.activate(this.id);
6095     },
6096
6097     /**
6098      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6099      */
6100     disable : function(){
6101         if(this.tabPanel.active != this){
6102             this.disabled = true;
6103             this.pnode.addClass("disabled");
6104         }
6105     },
6106
6107     /**
6108      * Enables this TabPanelItem if it was previously disabled.
6109      */
6110     enable : function(){
6111         this.disabled = false;
6112         this.pnode.removeClass("disabled");
6113     },
6114
6115     /**
6116      * Sets the content for this TabPanelItem.
6117      * @param {String} content The content
6118      * @param {Boolean} loadScripts true to look for and load scripts
6119      */
6120     setContent : function(content, loadScripts){
6121         this.bodyEl.update(content, loadScripts);
6122     },
6123
6124     /**
6125      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6126      * @return {Roo.UpdateManager} The UpdateManager
6127      */
6128     getUpdateManager : function(){
6129         return this.bodyEl.getUpdateManager();
6130     },
6131
6132     /**
6133      * Set a URL to be used to load the content for this TabPanelItem.
6134      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6135      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
6136      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
6137      * @return {Roo.UpdateManager} The UpdateManager
6138      */
6139     setUrl : function(url, params, loadOnce){
6140         if(this.refreshDelegate){
6141             this.un('activate', this.refreshDelegate);
6142         }
6143         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6144         this.on("activate", this.refreshDelegate);
6145         return this.bodyEl.getUpdateManager();
6146     },
6147
6148     /** @private */
6149     _handleRefresh : function(url, params, loadOnce){
6150         if(!loadOnce || !this.loaded){
6151             var updater = this.bodyEl.getUpdateManager();
6152             updater.update(url, params, this._setLoaded.createDelegate(this));
6153         }
6154     },
6155
6156     /**
6157      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6158      *   Will fail silently if the setUrl method has not been called.
6159      *   This does not activate the panel, just updates its content.
6160      */
6161     refresh : function(){
6162         if(this.refreshDelegate){
6163            this.loaded = false;
6164            this.refreshDelegate();
6165         }
6166     },
6167
6168     /** @private */
6169     _setLoaded : function(){
6170         this.loaded = true;
6171     },
6172
6173     /** @private */
6174     closeClick : function(e){
6175         var o = {};
6176         e.stopEvent();
6177         this.fireEvent("beforeclose", this, o);
6178         if(o.cancel !== true){
6179             this.tabPanel.removeTab(this.id);
6180         }
6181     },
6182     /**
6183      * The text displayed in the tooltip for the close icon.
6184      * @type String
6185      */
6186     closeText : "Close this tab"
6187 });
6188
6189 /** @private */
6190 Roo.TabPanel.prototype.createStrip = function(container){
6191     var strip = document.createElement("div");
6192     strip.className = "x-tabs-wrap";
6193     container.appendChild(strip);
6194     return strip;
6195 };
6196 /** @private */
6197 Roo.TabPanel.prototype.createStripList = function(strip){
6198     // div wrapper for retard IE
6199     // returns the "tr" element.
6200     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6201         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6202         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6203     return strip.firstChild.firstChild.firstChild.firstChild;
6204 };
6205 /** @private */
6206 Roo.TabPanel.prototype.createBody = function(container){
6207     var body = document.createElement("div");
6208     Roo.id(body, "tab-body");
6209     Roo.fly(body).addClass("x-tabs-body");
6210     container.appendChild(body);
6211     return body;
6212 };
6213 /** @private */
6214 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6215     var body = Roo.getDom(id);
6216     if(!body){
6217         body = document.createElement("div");
6218         body.id = id;
6219     }
6220     Roo.fly(body).addClass("x-tabs-item-body");
6221     bodyEl.insertBefore(body, bodyEl.firstChild);
6222     return body;
6223 };
6224 /** @private */
6225 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6226     var td = document.createElement("td");
6227     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6228     //stripEl.appendChild(td);
6229     if(closable){
6230         td.className = "x-tabs-closable";
6231         if(!this.closeTpl){
6232             this.closeTpl = new Roo.Template(
6233                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6234                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6235                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6236             );
6237         }
6238         var el = this.closeTpl.overwrite(td, {"text": text});
6239         var close = el.getElementsByTagName("div")[0];
6240         var inner = el.getElementsByTagName("em")[0];
6241         return {"el": el, "close": close, "inner": inner};
6242     } else {
6243         if(!this.tabTpl){
6244             this.tabTpl = new Roo.Template(
6245                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6246                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6247             );
6248         }
6249         var el = this.tabTpl.overwrite(td, {"text": text});
6250         var inner = el.getElementsByTagName("em")[0];
6251         return {"el": el, "inner": inner};
6252     }
6253 };/*
6254  * Based on:
6255  * Ext JS Library 1.1.1
6256  * Copyright(c) 2006-2007, Ext JS, LLC.
6257  *
6258  * Originally Released Under LGPL - original licence link has changed is not relivant.
6259  *
6260  * Fork - LGPL
6261  * <script type="text/javascript">
6262  */
6263
6264 /**
6265  * @class Roo.Button
6266  * @extends Roo.util.Observable
6267  * Simple Button class
6268  * @cfg {String} text The button text
6269  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6270  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6271  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6272  * @cfg {Object} scope The scope of the handler
6273  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6274  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6275  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6276  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6277  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6278  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6279    applies if enableToggle = true)
6280  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6281  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6282   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6283  * @constructor
6284  * Create a new button
6285  * @param {Object} config The config object
6286  */
6287 Roo.Button = function(renderTo, config)
6288 {
6289     if (!config) {
6290         config = renderTo;
6291         renderTo = config.renderTo || false;
6292     }
6293     
6294     Roo.apply(this, config);
6295     this.addEvents({
6296         /**
6297              * @event click
6298              * Fires when this button is clicked
6299              * @param {Button} this
6300              * @param {EventObject} e The click event
6301              */
6302             "click" : true,
6303         /**
6304              * @event toggle
6305              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6306              * @param {Button} this
6307              * @param {Boolean} pressed
6308              */
6309             "toggle" : true,
6310         /**
6311              * @event mouseover
6312              * Fires when the mouse hovers over the button
6313              * @param {Button} this
6314              * @param {Event} e The event object
6315              */
6316         'mouseover' : true,
6317         /**
6318              * @event mouseout
6319              * Fires when the mouse exits the button
6320              * @param {Button} this
6321              * @param {Event} e The event object
6322              */
6323         'mouseout': true,
6324          /**
6325              * @event render
6326              * Fires when the button is rendered
6327              * @param {Button} this
6328              */
6329         'render': true
6330     });
6331     if(this.menu){
6332         this.menu = Roo.menu.MenuMgr.get(this.menu);
6333     }
6334     // register listeners first!!  - so render can be captured..
6335     Roo.util.Observable.call(this);
6336     if(renderTo){
6337         this.render(renderTo);
6338     }
6339     
6340   
6341 };
6342
6343 Roo.extend(Roo.Button, Roo.util.Observable, {
6344     /**
6345      * 
6346      */
6347     
6348     /**
6349      * Read-only. True if this button is hidden
6350      * @type Boolean
6351      */
6352     hidden : false,
6353     /**
6354      * Read-only. True if this button is disabled
6355      * @type Boolean
6356      */
6357     disabled : false,
6358     /**
6359      * Read-only. True if this button is pressed (only if enableToggle = true)
6360      * @type Boolean
6361      */
6362     pressed : false,
6363
6364     /**
6365      * @cfg {Number} tabIndex 
6366      * The DOM tabIndex for this button (defaults to undefined)
6367      */
6368     tabIndex : undefined,
6369
6370     /**
6371      * @cfg {Boolean} enableToggle
6372      * True to enable pressed/not pressed toggling (defaults to false)
6373      */
6374     enableToggle: false,
6375     /**
6376      * @cfg {Roo.menu.Menu} menu
6377      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6378      */
6379     menu : undefined,
6380     /**
6381      * @cfg {String} menuAlign
6382      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6383      */
6384     menuAlign : "tl-bl?",
6385
6386     /**
6387      * @cfg {String} iconCls
6388      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6389      */
6390     iconCls : undefined,
6391     /**
6392      * @cfg {String} type
6393      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6394      */
6395     type : 'button',
6396
6397     // private
6398     menuClassTarget: 'tr',
6399
6400     /**
6401      * @cfg {String} clickEvent
6402      * The type of event to map to the button's event handler (defaults to 'click')
6403      */
6404     clickEvent : 'click',
6405
6406     /**
6407      * @cfg {Boolean} handleMouseEvents
6408      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6409      */
6410     handleMouseEvents : true,
6411
6412     /**
6413      * @cfg {String} tooltipType
6414      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6415      */
6416     tooltipType : 'qtip',
6417
6418     /**
6419      * @cfg {String} cls
6420      * A CSS class to apply to the button's main element.
6421      */
6422     
6423     /**
6424      * @cfg {Roo.Template} template (Optional)
6425      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6426      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6427      * require code modifications if required elements (e.g. a button) aren't present.
6428      */
6429
6430     // private
6431     render : function(renderTo){
6432         var btn;
6433         if(this.hideParent){
6434             this.parentEl = Roo.get(renderTo);
6435         }
6436         if(!this.dhconfig){
6437             if(!this.template){
6438                 if(!Roo.Button.buttonTemplate){
6439                     // hideous table template
6440                     Roo.Button.buttonTemplate = new Roo.Template(
6441                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6442                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
6443                         "</tr></tbody></table>");
6444                 }
6445                 this.template = Roo.Button.buttonTemplate;
6446             }
6447             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6448             var btnEl = btn.child("button:first");
6449             btnEl.on('focus', this.onFocus, this);
6450             btnEl.on('blur', this.onBlur, this);
6451             if(this.cls){
6452                 btn.addClass(this.cls);
6453             }
6454             if(this.icon){
6455                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6456             }
6457             if(this.iconCls){
6458                 btnEl.addClass(this.iconCls);
6459                 if(!this.cls){
6460                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6461                 }
6462             }
6463             if(this.tabIndex !== undefined){
6464                 btnEl.dom.tabIndex = this.tabIndex;
6465             }
6466             if(this.tooltip){
6467                 if(typeof this.tooltip == 'object'){
6468                     Roo.QuickTips.tips(Roo.apply({
6469                           target: btnEl.id
6470                     }, this.tooltip));
6471                 } else {
6472                     btnEl.dom[this.tooltipType] = this.tooltip;
6473                 }
6474             }
6475         }else{
6476             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6477         }
6478         this.el = btn;
6479         if(this.id){
6480             this.el.dom.id = this.el.id = this.id;
6481         }
6482         if(this.menu){
6483             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6484             this.menu.on("show", this.onMenuShow, this);
6485             this.menu.on("hide", this.onMenuHide, this);
6486         }
6487         btn.addClass("x-btn");
6488         if(Roo.isIE && !Roo.isIE7){
6489             this.autoWidth.defer(1, this);
6490         }else{
6491             this.autoWidth();
6492         }
6493         if(this.handleMouseEvents){
6494             btn.on("mouseover", this.onMouseOver, this);
6495             btn.on("mouseout", this.onMouseOut, this);
6496             btn.on("mousedown", this.onMouseDown, this);
6497         }
6498         btn.on(this.clickEvent, this.onClick, this);
6499         //btn.on("mouseup", this.onMouseUp, this);
6500         if(this.hidden){
6501             this.hide();
6502         }
6503         if(this.disabled){
6504             this.disable();
6505         }
6506         Roo.ButtonToggleMgr.register(this);
6507         if(this.pressed){
6508             this.el.addClass("x-btn-pressed");
6509         }
6510         if(this.repeat){
6511             var repeater = new Roo.util.ClickRepeater(btn,
6512                 typeof this.repeat == "object" ? this.repeat : {}
6513             );
6514             repeater.on("click", this.onClick,  this);
6515         }
6516         
6517         this.fireEvent('render', this);
6518         
6519     },
6520     /**
6521      * Returns the button's underlying element
6522      * @return {Roo.Element} The element
6523      */
6524     getEl : function(){
6525         return this.el;  
6526     },
6527     
6528     /**
6529      * Destroys this Button and removes any listeners.
6530      */
6531     destroy : function(){
6532         Roo.ButtonToggleMgr.unregister(this);
6533         this.el.removeAllListeners();
6534         this.purgeListeners();
6535         this.el.remove();
6536     },
6537
6538     // private
6539     autoWidth : function(){
6540         if(this.el){
6541             this.el.setWidth("auto");
6542             if(Roo.isIE7 && Roo.isStrict){
6543                 var ib = this.el.child('button');
6544                 if(ib && ib.getWidth() > 20){
6545                     ib.clip();
6546                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6547                 }
6548             }
6549             if(this.minWidth){
6550                 if(this.hidden){
6551                     this.el.beginMeasure();
6552                 }
6553                 if(this.el.getWidth() < this.minWidth){
6554                     this.el.setWidth(this.minWidth);
6555                 }
6556                 if(this.hidden){
6557                     this.el.endMeasure();
6558                 }
6559             }
6560         }
6561     },
6562
6563     /**
6564      * Assigns this button's click handler
6565      * @param {Function} handler The function to call when the button is clicked
6566      * @param {Object} scope (optional) Scope for the function passed in
6567      */
6568     setHandler : function(handler, scope){
6569         this.handler = handler;
6570         this.scope = scope;  
6571     },
6572     
6573     /**
6574      * Sets this button's text
6575      * @param {String} text The button text
6576      */
6577     setText : function(text){
6578         this.text = text;
6579         if(this.el){
6580             this.el.child("td.x-btn-center button.x-btn-text").update(text);
6581         }
6582         this.autoWidth();
6583     },
6584     
6585     /**
6586      * Gets the text for this button
6587      * @return {String} The button text
6588      */
6589     getText : function(){
6590         return this.text;  
6591     },
6592     
6593     /**
6594      * Show this button
6595      */
6596     show: function(){
6597         this.hidden = false;
6598         if(this.el){
6599             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
6600         }
6601     },
6602     
6603     /**
6604      * Hide this button
6605      */
6606     hide: function(){
6607         this.hidden = true;
6608         if(this.el){
6609             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
6610         }
6611     },
6612     
6613     /**
6614      * Convenience function for boolean show/hide
6615      * @param {Boolean} visible True to show, false to hide
6616      */
6617     setVisible: function(visible){
6618         if(visible) {
6619             this.show();
6620         }else{
6621             this.hide();
6622         }
6623     },
6624     
6625     /**
6626      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
6627      * @param {Boolean} state (optional) Force a particular state
6628      */
6629     toggle : function(state){
6630         state = state === undefined ? !this.pressed : state;
6631         if(state != this.pressed){
6632             if(state){
6633                 this.el.addClass("x-btn-pressed");
6634                 this.pressed = true;
6635                 this.fireEvent("toggle", this, true);
6636             }else{
6637                 this.el.removeClass("x-btn-pressed");
6638                 this.pressed = false;
6639                 this.fireEvent("toggle", this, false);
6640             }
6641             if(this.toggleHandler){
6642                 this.toggleHandler.call(this.scope || this, this, state);
6643             }
6644         }
6645     },
6646     
6647     /**
6648      * Focus the button
6649      */
6650     focus : function(){
6651         this.el.child('button:first').focus();
6652     },
6653     
6654     /**
6655      * Disable this button
6656      */
6657     disable : function(){
6658         if(this.el){
6659             this.el.addClass("x-btn-disabled");
6660         }
6661         this.disabled = true;
6662     },
6663     
6664     /**
6665      * Enable this button
6666      */
6667     enable : function(){
6668         if(this.el){
6669             this.el.removeClass("x-btn-disabled");
6670         }
6671         this.disabled = false;
6672     },
6673
6674     /**
6675      * Convenience function for boolean enable/disable
6676      * @param {Boolean} enabled True to enable, false to disable
6677      */
6678     setDisabled : function(v){
6679         this[v !== true ? "enable" : "disable"]();
6680     },
6681
6682     // private
6683     onClick : function(e)
6684     {
6685         if(e){
6686             e.preventDefault();
6687         }
6688         if(e.button != 0){
6689             return;
6690         }
6691         if(!this.disabled){
6692             if(this.enableToggle){
6693                 this.toggle();
6694             }
6695             if(this.menu && !this.menu.isVisible()){
6696                 this.menu.show(this.el, this.menuAlign);
6697             }
6698             this.fireEvent("click", this, e);
6699             if(this.handler){
6700                 this.el.removeClass("x-btn-over");
6701                 this.handler.call(this.scope || this, this, e);
6702             }
6703         }
6704     },
6705     // private
6706     onMouseOver : function(e){
6707         if(!this.disabled){
6708             this.el.addClass("x-btn-over");
6709             this.fireEvent('mouseover', this, e);
6710         }
6711     },
6712     // private
6713     onMouseOut : function(e){
6714         if(!e.within(this.el,  true)){
6715             this.el.removeClass("x-btn-over");
6716             this.fireEvent('mouseout', this, e);
6717         }
6718     },
6719     // private
6720     onFocus : function(e){
6721         if(!this.disabled){
6722             this.el.addClass("x-btn-focus");
6723         }
6724     },
6725     // private
6726     onBlur : function(e){
6727         this.el.removeClass("x-btn-focus");
6728     },
6729     // private
6730     onMouseDown : function(e){
6731         if(!this.disabled && e.button == 0){
6732             this.el.addClass("x-btn-click");
6733             Roo.get(document).on('mouseup', this.onMouseUp, this);
6734         }
6735     },
6736     // private
6737     onMouseUp : function(e){
6738         if(e.button == 0){
6739             this.el.removeClass("x-btn-click");
6740             Roo.get(document).un('mouseup', this.onMouseUp, this);
6741         }
6742     },
6743     // private
6744     onMenuShow : function(e){
6745         this.el.addClass("x-btn-menu-active");
6746     },
6747     // private
6748     onMenuHide : function(e){
6749         this.el.removeClass("x-btn-menu-active");
6750     }   
6751 });
6752
6753 // Private utility class used by Button
6754 Roo.ButtonToggleMgr = function(){
6755    var groups = {};
6756    
6757    function toggleGroup(btn, state){
6758        if(state){
6759            var g = groups[btn.toggleGroup];
6760            for(var i = 0, l = g.length; i < l; i++){
6761                if(g[i] != btn){
6762                    g[i].toggle(false);
6763                }
6764            }
6765        }
6766    }
6767    
6768    return {
6769        register : function(btn){
6770            if(!btn.toggleGroup){
6771                return;
6772            }
6773            var g = groups[btn.toggleGroup];
6774            if(!g){
6775                g = groups[btn.toggleGroup] = [];
6776            }
6777            g.push(btn);
6778            btn.on("toggle", toggleGroup);
6779        },
6780        
6781        unregister : function(btn){
6782            if(!btn.toggleGroup){
6783                return;
6784            }
6785            var g = groups[btn.toggleGroup];
6786            if(g){
6787                g.remove(btn);
6788                btn.un("toggle", toggleGroup);
6789            }
6790        }
6791    };
6792 }();/*
6793  * Based on:
6794  * Ext JS Library 1.1.1
6795  * Copyright(c) 2006-2007, Ext JS, LLC.
6796  *
6797  * Originally Released Under LGPL - original licence link has changed is not relivant.
6798  *
6799  * Fork - LGPL
6800  * <script type="text/javascript">
6801  */
6802  
6803 /**
6804  * @class Roo.SplitButton
6805  * @extends Roo.Button
6806  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
6807  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
6808  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
6809  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
6810  * @cfg {String} arrowTooltip The title attribute of the arrow
6811  * @constructor
6812  * Create a new menu button
6813  * @param {String/HTMLElement/Element} renderTo The element to append the button to
6814  * @param {Object} config The config object
6815  */
6816 Roo.SplitButton = function(renderTo, config){
6817     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
6818     /**
6819      * @event arrowclick
6820      * Fires when this button's arrow is clicked
6821      * @param {SplitButton} this
6822      * @param {EventObject} e The click event
6823      */
6824     this.addEvents({"arrowclick":true});
6825 };
6826
6827 Roo.extend(Roo.SplitButton, Roo.Button, {
6828     render : function(renderTo){
6829         // this is one sweet looking template!
6830         var tpl = new Roo.Template(
6831             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
6832             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
6833             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
6834             "</tbody></table></td><td>",
6835             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
6836             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
6837             "</tbody></table></td></tr></table>"
6838         );
6839         var btn = tpl.append(renderTo, [this.text, this.type], true);
6840         var btnEl = btn.child("button");
6841         if(this.cls){
6842             btn.addClass(this.cls);
6843         }
6844         if(this.icon){
6845             btnEl.setStyle('background-image', 'url(' +this.icon +')');
6846         }
6847         if(this.iconCls){
6848             btnEl.addClass(this.iconCls);
6849             if(!this.cls){
6850                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6851             }
6852         }
6853         this.el = btn;
6854         if(this.handleMouseEvents){
6855             btn.on("mouseover", this.onMouseOver, this);
6856             btn.on("mouseout", this.onMouseOut, this);
6857             btn.on("mousedown", this.onMouseDown, this);
6858             btn.on("mouseup", this.onMouseUp, this);
6859         }
6860         btn.on(this.clickEvent, this.onClick, this);
6861         if(this.tooltip){
6862             if(typeof this.tooltip == 'object'){
6863                 Roo.QuickTips.tips(Roo.apply({
6864                       target: btnEl.id
6865                 }, this.tooltip));
6866             } else {
6867                 btnEl.dom[this.tooltipType] = this.tooltip;
6868             }
6869         }
6870         if(this.arrowTooltip){
6871             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
6872         }
6873         if(this.hidden){
6874             this.hide();
6875         }
6876         if(this.disabled){
6877             this.disable();
6878         }
6879         if(this.pressed){
6880             this.el.addClass("x-btn-pressed");
6881         }
6882         if(Roo.isIE && !Roo.isIE7){
6883             this.autoWidth.defer(1, this);
6884         }else{
6885             this.autoWidth();
6886         }
6887         if(this.menu){
6888             this.menu.on("show", this.onMenuShow, this);
6889             this.menu.on("hide", this.onMenuHide, this);
6890         }
6891         this.fireEvent('render', this);
6892     },
6893
6894     // private
6895     autoWidth : function(){
6896         if(this.el){
6897             var tbl = this.el.child("table:first");
6898             var tbl2 = this.el.child("table:last");
6899             this.el.setWidth("auto");
6900             tbl.setWidth("auto");
6901             if(Roo.isIE7 && Roo.isStrict){
6902                 var ib = this.el.child('button:first');
6903                 if(ib && ib.getWidth() > 20){
6904                     ib.clip();
6905                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6906                 }
6907             }
6908             if(this.minWidth){
6909                 if(this.hidden){
6910                     this.el.beginMeasure();
6911                 }
6912                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
6913                     tbl.setWidth(this.minWidth-tbl2.getWidth());
6914                 }
6915                 if(this.hidden){
6916                     this.el.endMeasure();
6917                 }
6918             }
6919             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
6920         } 
6921     },
6922     /**
6923      * Sets this button's click handler
6924      * @param {Function} handler The function to call when the button is clicked
6925      * @param {Object} scope (optional) Scope for the function passed above
6926      */
6927     setHandler : function(handler, scope){
6928         this.handler = handler;
6929         this.scope = scope;  
6930     },
6931     
6932     /**
6933      * Sets this button's arrow click handler
6934      * @param {Function} handler The function to call when the arrow is clicked
6935      * @param {Object} scope (optional) Scope for the function passed above
6936      */
6937     setArrowHandler : function(handler, scope){
6938         this.arrowHandler = handler;
6939         this.scope = scope;  
6940     },
6941     
6942     /**
6943      * Focus the button
6944      */
6945     focus : function(){
6946         if(this.el){
6947             this.el.child("button:first").focus();
6948         }
6949     },
6950
6951     // private
6952     onClick : function(e){
6953         e.preventDefault();
6954         if(!this.disabled){
6955             if(e.getTarget(".x-btn-menu-arrow-wrap")){
6956                 if(this.menu && !this.menu.isVisible()){
6957                     this.menu.show(this.el, this.menuAlign);
6958                 }
6959                 this.fireEvent("arrowclick", this, e);
6960                 if(this.arrowHandler){
6961                     this.arrowHandler.call(this.scope || this, this, e);
6962                 }
6963             }else{
6964                 this.fireEvent("click", this, e);
6965                 if(this.handler){
6966                     this.handler.call(this.scope || this, this, e);
6967                 }
6968             }
6969         }
6970     },
6971     // private
6972     onMouseDown : function(e){
6973         if(!this.disabled){
6974             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
6975         }
6976     },
6977     // private
6978     onMouseUp : function(e){
6979         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
6980     }   
6981 });
6982
6983
6984 // backwards compat
6985 Roo.MenuButton = Roo.SplitButton;/*
6986  * Based on:
6987  * Ext JS Library 1.1.1
6988  * Copyright(c) 2006-2007, Ext JS, LLC.
6989  *
6990  * Originally Released Under LGPL - original licence link has changed is not relivant.
6991  *
6992  * Fork - LGPL
6993  * <script type="text/javascript">
6994  */
6995
6996 /**
6997  * @class Roo.Toolbar
6998  * @children   Roo.Toolbar.Item Roo.form.Field
6999  * Basic Toolbar class.
7000  * @constructor
7001  * Creates a new Toolbar
7002  * @param {Object} container The config object
7003  */ 
7004 Roo.Toolbar = function(container, buttons, config)
7005 {
7006     /// old consturctor format still supported..
7007     if(container instanceof Array){ // omit the container for later rendering
7008         buttons = container;
7009         config = buttons;
7010         container = null;
7011     }
7012     if (typeof(container) == 'object' && container.xtype) {
7013         config = container;
7014         container = config.container;
7015         buttons = config.buttons || []; // not really - use items!!
7016     }
7017     var xitems = [];
7018     if (config && config.items) {
7019         xitems = config.items;
7020         delete config.items;
7021     }
7022     Roo.apply(this, config);
7023     this.buttons = buttons;
7024     
7025     if(container){
7026         this.render(container);
7027     }
7028     this.xitems = xitems;
7029     Roo.each(xitems, function(b) {
7030         this.add(b);
7031     }, this);
7032     
7033 };
7034
7035 Roo.Toolbar.prototype = {
7036     /**
7037      * @cfg {Array} items
7038      * array of button configs or elements to add (will be converted to a MixedCollection)
7039      */
7040     items: false,
7041     /**
7042      * @cfg {String/HTMLElement/Element} container
7043      * The id or element that will contain the toolbar
7044      */
7045     // private
7046     render : function(ct){
7047         this.el = Roo.get(ct);
7048         if(this.cls){
7049             this.el.addClass(this.cls);
7050         }
7051         // using a table allows for vertical alignment
7052         // 100% width is needed by Safari...
7053         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7054         this.tr = this.el.child("tr", true);
7055         var autoId = 0;
7056         this.items = new Roo.util.MixedCollection(false, function(o){
7057             return o.id || ("item" + (++autoId));
7058         });
7059         if(this.buttons){
7060             this.add.apply(this, this.buttons);
7061             delete this.buttons;
7062         }
7063     },
7064
7065     /**
7066      * Adds element(s) to the toolbar -- this function takes a variable number of 
7067      * arguments of mixed type and adds them to the toolbar.
7068      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7069      * <ul>
7070      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7071      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7072      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7073      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7074      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7075      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7076      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7077      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7078      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7079      * </ul>
7080      * @param {Mixed} arg2
7081      * @param {Mixed} etc.
7082      */
7083     add : function(){
7084         var a = arguments, l = a.length;
7085         for(var i = 0; i < l; i++){
7086             this._add(a[i]);
7087         }
7088     },
7089     // private..
7090     _add : function(el) {
7091         
7092         if (el.xtype) {
7093             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7094         }
7095         
7096         if (el.applyTo){ // some kind of form field
7097             return this.addField(el);
7098         } 
7099         if (el.render){ // some kind of Toolbar.Item
7100             return this.addItem(el);
7101         }
7102         if (typeof el == "string"){ // string
7103             if(el == "separator" || el == "-"){
7104                 return this.addSeparator();
7105             }
7106             if (el == " "){
7107                 return this.addSpacer();
7108             }
7109             if(el == "->"){
7110                 return this.addFill();
7111             }
7112             return this.addText(el);
7113             
7114         }
7115         if(el.tagName){ // element
7116             return this.addElement(el);
7117         }
7118         if(typeof el == "object"){ // must be button config?
7119             return this.addButton(el);
7120         }
7121         // and now what?!?!
7122         return false;
7123         
7124     },
7125     
7126     /**
7127      * Add an Xtype element
7128      * @param {Object} xtype Xtype Object
7129      * @return {Object} created Object
7130      */
7131     addxtype : function(e){
7132         return this.add(e);  
7133     },
7134     
7135     /**
7136      * Returns the Element for this toolbar.
7137      * @return {Roo.Element}
7138      */
7139     getEl : function(){
7140         return this.el;  
7141     },
7142     
7143     /**
7144      * Adds a separator
7145      * @return {Roo.Toolbar.Item} The separator item
7146      */
7147     addSeparator : function(){
7148         return this.addItem(new Roo.Toolbar.Separator());
7149     },
7150
7151     /**
7152      * Adds a spacer element
7153      * @return {Roo.Toolbar.Spacer} The spacer item
7154      */
7155     addSpacer : function(){
7156         return this.addItem(new Roo.Toolbar.Spacer());
7157     },
7158
7159     /**
7160      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7161      * @return {Roo.Toolbar.Fill} The fill item
7162      */
7163     addFill : function(){
7164         return this.addItem(new Roo.Toolbar.Fill());
7165     },
7166
7167     /**
7168      * Adds any standard HTML element to the toolbar
7169      * @param {String/HTMLElement/Element} el The element or id of the element to add
7170      * @return {Roo.Toolbar.Item} The element's item
7171      */
7172     addElement : function(el){
7173         return this.addItem(new Roo.Toolbar.Item(el));
7174     },
7175     /**
7176      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7177      * @type Roo.util.MixedCollection  
7178      */
7179     items : false,
7180      
7181     /**
7182      * Adds any Toolbar.Item or subclass
7183      * @param {Roo.Toolbar.Item} item
7184      * @return {Roo.Toolbar.Item} The item
7185      */
7186     addItem : function(item){
7187         var td = this.nextBlock();
7188         item.render(td);
7189         this.items.add(item);
7190         return item;
7191     },
7192     
7193     /**
7194      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7195      * @param {Object/Array} config A button config or array of configs
7196      * @return {Roo.Toolbar.Button/Array}
7197      */
7198     addButton : function(config){
7199         if(config instanceof Array){
7200             var buttons = [];
7201             for(var i = 0, len = config.length; i < len; i++) {
7202                 buttons.push(this.addButton(config[i]));
7203             }
7204             return buttons;
7205         }
7206         var b = config;
7207         if(!(config instanceof Roo.Toolbar.Button)){
7208             b = config.split ?
7209                 new Roo.Toolbar.SplitButton(config) :
7210                 new Roo.Toolbar.Button(config);
7211         }
7212         var td = this.nextBlock();
7213         b.render(td);
7214         this.items.add(b);
7215         return b;
7216     },
7217     
7218     /**
7219      * Adds text to the toolbar
7220      * @param {String} text The text to add
7221      * @return {Roo.Toolbar.Item} The element's item
7222      */
7223     addText : function(text){
7224         return this.addItem(new Roo.Toolbar.TextItem(text));
7225     },
7226     
7227     /**
7228      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7229      * @param {Number} index The index where the item is to be inserted
7230      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7231      * @return {Roo.Toolbar.Button/Item}
7232      */
7233     insertButton : function(index, item){
7234         if(item instanceof Array){
7235             var buttons = [];
7236             for(var i = 0, len = item.length; i < len; i++) {
7237                buttons.push(this.insertButton(index + i, item[i]));
7238             }
7239             return buttons;
7240         }
7241         if (!(item instanceof Roo.Toolbar.Button)){
7242            item = new Roo.Toolbar.Button(item);
7243         }
7244         var td = document.createElement("td");
7245         this.tr.insertBefore(td, this.tr.childNodes[index]);
7246         item.render(td);
7247         this.items.insert(index, item);
7248         return item;
7249     },
7250     
7251     /**
7252      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7253      * @param {Object} config
7254      * @return {Roo.Toolbar.Item} The element's item
7255      */
7256     addDom : function(config, returnEl){
7257         var td = this.nextBlock();
7258         Roo.DomHelper.overwrite(td, config);
7259         var ti = new Roo.Toolbar.Item(td.firstChild);
7260         ti.render(td);
7261         this.items.add(ti);
7262         return ti;
7263     },
7264
7265     /**
7266      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7267      * @type Roo.util.MixedCollection  
7268      */
7269     fields : false,
7270     
7271     /**
7272      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7273      * Note: the field should not have been rendered yet. For a field that has already been
7274      * rendered, use {@link #addElement}.
7275      * @param {Roo.form.Field} field
7276      * @return {Roo.ToolbarItem}
7277      */
7278      
7279       
7280     addField : function(field) {
7281         if (!this.fields) {
7282             var autoId = 0;
7283             this.fields = new Roo.util.MixedCollection(false, function(o){
7284                 return o.id || ("item" + (++autoId));
7285             });
7286
7287         }
7288         
7289         var td = this.nextBlock();
7290         field.render(td);
7291         var ti = new Roo.Toolbar.Item(td.firstChild);
7292         ti.render(td);
7293         this.items.add(ti);
7294         this.fields.add(field);
7295         return ti;
7296     },
7297     /**
7298      * Hide the toolbar
7299      * @method hide
7300      */
7301      
7302       
7303     hide : function()
7304     {
7305         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7306         this.el.child('div').hide();
7307     },
7308     /**
7309      * Show the toolbar
7310      * @method show
7311      */
7312     show : function()
7313     {
7314         this.el.child('div').show();
7315     },
7316       
7317     // private
7318     nextBlock : function(){
7319         var td = document.createElement("td");
7320         this.tr.appendChild(td);
7321         return td;
7322     },
7323
7324     // private
7325     destroy : function(){
7326         if(this.items){ // rendered?
7327             Roo.destroy.apply(Roo, this.items.items);
7328         }
7329         if(this.fields){ // rendered?
7330             Roo.destroy.apply(Roo, this.fields.items);
7331         }
7332         Roo.Element.uncache(this.el, this.tr);
7333     }
7334 };
7335
7336 /**
7337  * @class Roo.Toolbar.Item
7338  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7339  * @constructor
7340  * Creates a new Item
7341  * @param {HTMLElement} el 
7342  */
7343 Roo.Toolbar.Item = function(el){
7344     var cfg = {};
7345     if (typeof (el.xtype) != 'undefined') {
7346         cfg = el;
7347         el = cfg.el;
7348     }
7349     
7350     this.el = Roo.getDom(el);
7351     this.id = Roo.id(this.el);
7352     this.hidden = false;
7353     
7354     this.addEvents({
7355          /**
7356              * @event render
7357              * Fires when the button is rendered
7358              * @param {Button} this
7359              */
7360         'render': true
7361     });
7362     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7363 };
7364 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7365 //Roo.Toolbar.Item.prototype = {
7366     
7367     /**
7368      * Get this item's HTML Element
7369      * @return {HTMLElement}
7370      */
7371     getEl : function(){
7372        return this.el;  
7373     },
7374
7375     // private
7376     render : function(td){
7377         
7378          this.td = td;
7379         td.appendChild(this.el);
7380         
7381         this.fireEvent('render', this);
7382     },
7383     
7384     /**
7385      * Removes and destroys this item.
7386      */
7387     destroy : function(){
7388         this.td.parentNode.removeChild(this.td);
7389     },
7390     
7391     /**
7392      * Shows this item.
7393      */
7394     show: function(){
7395         this.hidden = false;
7396         this.td.style.display = "";
7397     },
7398     
7399     /**
7400      * Hides this item.
7401      */
7402     hide: function(){
7403         this.hidden = true;
7404         this.td.style.display = "none";
7405     },
7406     
7407     /**
7408      * Convenience function for boolean show/hide.
7409      * @param {Boolean} visible true to show/false to hide
7410      */
7411     setVisible: function(visible){
7412         if(visible) {
7413             this.show();
7414         }else{
7415             this.hide();
7416         }
7417     },
7418     
7419     /**
7420      * Try to focus this item.
7421      */
7422     focus : function(){
7423         Roo.fly(this.el).focus();
7424     },
7425     
7426     /**
7427      * Disables this item.
7428      */
7429     disable : function(){
7430         Roo.fly(this.td).addClass("x-item-disabled");
7431         this.disabled = true;
7432         this.el.disabled = true;
7433     },
7434     
7435     /**
7436      * Enables this item.
7437      */
7438     enable : function(){
7439         Roo.fly(this.td).removeClass("x-item-disabled");
7440         this.disabled = false;
7441         this.el.disabled = false;
7442     }
7443 });
7444
7445
7446 /**
7447  * @class Roo.Toolbar.Separator
7448  * @extends Roo.Toolbar.Item
7449  * A simple toolbar separator class
7450  * @constructor
7451  * Creates a new Separator
7452  */
7453 Roo.Toolbar.Separator = function(cfg){
7454     
7455     var s = document.createElement("span");
7456     s.className = "ytb-sep";
7457     if (cfg) {
7458         cfg.el = s;
7459     }
7460     
7461     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7462 };
7463 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7464     enable:Roo.emptyFn,
7465     disable:Roo.emptyFn,
7466     focus:Roo.emptyFn
7467 });
7468
7469 /**
7470  * @class Roo.Toolbar.Spacer
7471  * @extends Roo.Toolbar.Item
7472  * A simple element that adds extra horizontal space to a toolbar.
7473  * @constructor
7474  * Creates a new Spacer
7475  */
7476 Roo.Toolbar.Spacer = function(cfg){
7477     var s = document.createElement("div");
7478     s.className = "ytb-spacer";
7479     if (cfg) {
7480         cfg.el = s;
7481     }
7482     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7483 };
7484 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7485     enable:Roo.emptyFn,
7486     disable:Roo.emptyFn,
7487     focus:Roo.emptyFn
7488 });
7489
7490 /**
7491  * @class Roo.Toolbar.Fill
7492  * @extends Roo.Toolbar.Spacer
7493  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7494  * @constructor
7495  * Creates a new Spacer
7496  */
7497 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7498     // private
7499     render : function(td){
7500         td.style.width = '100%';
7501         Roo.Toolbar.Fill.superclass.render.call(this, td);
7502     }
7503 });
7504
7505 /**
7506  * @class Roo.Toolbar.TextItem
7507  * @extends Roo.Toolbar.Item
7508  * A simple class that renders text directly into a toolbar.
7509  * @constructor
7510  * Creates a new TextItem
7511  * @cfg {string} text 
7512  */
7513 Roo.Toolbar.TextItem = function(cfg){
7514     var  text = cfg || "";
7515     if (typeof(cfg) == 'object') {
7516         text = cfg.text || "";
7517     }  else {
7518         cfg = null;
7519     }
7520     var s = document.createElement("span");
7521     s.className = "ytb-text";
7522     s.innerHTML = text;
7523     if (cfg) {
7524         cfg.el  = s;
7525     }
7526     
7527     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7528 };
7529 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7530     
7531      
7532     enable:Roo.emptyFn,
7533     disable:Roo.emptyFn,
7534     focus:Roo.emptyFn
7535 });
7536
7537 /**
7538  * @class Roo.Toolbar.Button
7539  * @extends Roo.Button
7540  * A button that renders into a toolbar.
7541  * @constructor
7542  * Creates a new Button
7543  * @param {Object} config A standard {@link Roo.Button} config object
7544  */
7545 Roo.Toolbar.Button = function(config){
7546     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7547 };
7548 Roo.extend(Roo.Toolbar.Button, Roo.Button,
7549 {
7550     
7551     
7552     render : function(td){
7553         this.td = td;
7554         Roo.Toolbar.Button.superclass.render.call(this, td);
7555     },
7556     
7557     /**
7558      * Removes and destroys this button
7559      */
7560     destroy : function(){
7561         Roo.Toolbar.Button.superclass.destroy.call(this);
7562         this.td.parentNode.removeChild(this.td);
7563     },
7564     
7565     /**
7566      * Shows this button
7567      */
7568     show: function(){
7569         this.hidden = false;
7570         this.td.style.display = "";
7571     },
7572     
7573     /**
7574      * Hides this button
7575      */
7576     hide: function(){
7577         this.hidden = true;
7578         this.td.style.display = "none";
7579     },
7580
7581     /**
7582      * Disables this item
7583      */
7584     disable : function(){
7585         Roo.fly(this.td).addClass("x-item-disabled");
7586         this.disabled = true;
7587     },
7588
7589     /**
7590      * Enables this item
7591      */
7592     enable : function(){
7593         Roo.fly(this.td).removeClass("x-item-disabled");
7594         this.disabled = false;
7595     }
7596 });
7597 // backwards compat
7598 Roo.ToolbarButton = Roo.Toolbar.Button;
7599
7600 /**
7601  * @class Roo.Toolbar.SplitButton
7602  * @extends Roo.SplitButton
7603  * A menu button that renders into a toolbar.
7604  * @constructor
7605  * Creates a new SplitButton
7606  * @param {Object} config A standard {@link Roo.SplitButton} config object
7607  */
7608 Roo.Toolbar.SplitButton = function(config){
7609     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
7610 };
7611 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
7612     render : function(td){
7613         this.td = td;
7614         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
7615     },
7616     
7617     /**
7618      * Removes and destroys this button
7619      */
7620     destroy : function(){
7621         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
7622         this.td.parentNode.removeChild(this.td);
7623     },
7624     
7625     /**
7626      * Shows this button
7627      */
7628     show: function(){
7629         this.hidden = false;
7630         this.td.style.display = "";
7631     },
7632     
7633     /**
7634      * Hides this button
7635      */
7636     hide: function(){
7637         this.hidden = true;
7638         this.td.style.display = "none";
7639     }
7640 });
7641
7642 // backwards compat
7643 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
7644  * Based on:
7645  * Ext JS Library 1.1.1
7646  * Copyright(c) 2006-2007, Ext JS, LLC.
7647  *
7648  * Originally Released Under LGPL - original licence link has changed is not relivant.
7649  *
7650  * Fork - LGPL
7651  * <script type="text/javascript">
7652  */
7653  
7654 /**
7655  * @class Roo.PagingToolbar
7656  * @extends Roo.Toolbar
7657  * @children   Roo.Toolbar.Item Roo.form.Field
7658  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
7659  * @constructor
7660  * Create a new PagingToolbar
7661  * @param {Object} config The config object
7662  */
7663 Roo.PagingToolbar = function(el, ds, config)
7664 {
7665     // old args format still supported... - xtype is prefered..
7666     if (typeof(el) == 'object' && el.xtype) {
7667         // created from xtype...
7668         config = el;
7669         ds = el.dataSource;
7670         el = config.container;
7671     }
7672     var items = [];
7673     if (config.items) {
7674         items = config.items;
7675         config.items = [];
7676     }
7677     
7678     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
7679     this.ds = ds;
7680     this.cursor = 0;
7681     this.renderButtons(this.el);
7682     this.bind(ds);
7683     
7684     // supprot items array.
7685    
7686     Roo.each(items, function(e) {
7687         this.add(Roo.factory(e));
7688     },this);
7689     
7690 };
7691
7692 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
7693    
7694     /**
7695      * @cfg {String/HTMLElement/Element} container
7696      * container The id or element that will contain the toolbar
7697      */
7698     /**
7699      * @cfg {Boolean} displayInfo
7700      * True to display the displayMsg (defaults to false)
7701      */
7702     
7703     
7704     /**
7705      * @cfg {Number} pageSize
7706      * The number of records to display per page (defaults to 20)
7707      */
7708     pageSize: 20,
7709     /**
7710      * @cfg {String} displayMsg
7711      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
7712      */
7713     displayMsg : 'Displaying {0} - {1} of {2}',
7714     /**
7715      * @cfg {String} emptyMsg
7716      * The message to display when no records are found (defaults to "No data to display")
7717      */
7718     emptyMsg : 'No data to display',
7719     /**
7720      * Customizable piece of the default paging text (defaults to "Page")
7721      * @type String
7722      */
7723     beforePageText : "Page",
7724     /**
7725      * Customizable piece of the default paging text (defaults to "of %0")
7726      * @type String
7727      */
7728     afterPageText : "of {0}",
7729     /**
7730      * Customizable piece of the default paging text (defaults to "First Page")
7731      * @type String
7732      */
7733     firstText : "First Page",
7734     /**
7735      * Customizable piece of the default paging text (defaults to "Previous Page")
7736      * @type String
7737      */
7738     prevText : "Previous Page",
7739     /**
7740      * Customizable piece of the default paging text (defaults to "Next Page")
7741      * @type String
7742      */
7743     nextText : "Next Page",
7744     /**
7745      * Customizable piece of the default paging text (defaults to "Last Page")
7746      * @type String
7747      */
7748     lastText : "Last Page",
7749     /**
7750      * Customizable piece of the default paging text (defaults to "Refresh")
7751      * @type String
7752      */
7753     refreshText : "Refresh",
7754
7755     // private
7756     renderButtons : function(el){
7757         Roo.PagingToolbar.superclass.render.call(this, el);
7758         this.first = this.addButton({
7759             tooltip: this.firstText,
7760             cls: "x-btn-icon x-grid-page-first",
7761             disabled: true,
7762             handler: this.onClick.createDelegate(this, ["first"])
7763         });
7764         this.prev = this.addButton({
7765             tooltip: this.prevText,
7766             cls: "x-btn-icon x-grid-page-prev",
7767             disabled: true,
7768             handler: this.onClick.createDelegate(this, ["prev"])
7769         });
7770         //this.addSeparator();
7771         this.add(this.beforePageText);
7772         this.field = Roo.get(this.addDom({
7773            tag: "input",
7774            type: "text",
7775            size: "3",
7776            value: "1",
7777            cls: "x-grid-page-number"
7778         }).el);
7779         this.field.on("keydown", this.onPagingKeydown, this);
7780         this.field.on("focus", function(){this.dom.select();});
7781         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
7782         this.field.setHeight(18);
7783         //this.addSeparator();
7784         this.next = this.addButton({
7785             tooltip: this.nextText,
7786             cls: "x-btn-icon x-grid-page-next",
7787             disabled: true,
7788             handler: this.onClick.createDelegate(this, ["next"])
7789         });
7790         this.last = this.addButton({
7791             tooltip: this.lastText,
7792             cls: "x-btn-icon x-grid-page-last",
7793             disabled: true,
7794             handler: this.onClick.createDelegate(this, ["last"])
7795         });
7796         //this.addSeparator();
7797         this.loading = this.addButton({
7798             tooltip: this.refreshText,
7799             cls: "x-btn-icon x-grid-loading",
7800             handler: this.onClick.createDelegate(this, ["refresh"])
7801         });
7802
7803         if(this.displayInfo){
7804             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
7805         }
7806     },
7807
7808     // private
7809     updateInfo : function(){
7810         if(this.displayEl){
7811             var count = this.ds.getCount();
7812             var msg = count == 0 ?
7813                 this.emptyMsg :
7814                 String.format(
7815                     this.displayMsg,
7816                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
7817                 );
7818             this.displayEl.update(msg);
7819         }
7820     },
7821
7822     // private
7823     onLoad : function(ds, r, o){
7824        this.cursor = o.params ? o.params.start : 0;
7825        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
7826
7827        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
7828        this.field.dom.value = ap;
7829        this.first.setDisabled(ap == 1);
7830        this.prev.setDisabled(ap == 1);
7831        this.next.setDisabled(ap == ps);
7832        this.last.setDisabled(ap == ps);
7833        this.loading.enable();
7834        this.updateInfo();
7835     },
7836
7837     // private
7838     getPageData : function(){
7839         var total = this.ds.getTotalCount();
7840         return {
7841             total : total,
7842             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
7843             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
7844         };
7845     },
7846
7847     // private
7848     onLoadError : function(){
7849         this.loading.enable();
7850     },
7851
7852     // private
7853     onPagingKeydown : function(e){
7854         var k = e.getKey();
7855         var d = this.getPageData();
7856         if(k == e.RETURN){
7857             var v = this.field.dom.value, pageNum;
7858             if(!v || isNaN(pageNum = parseInt(v, 10))){
7859                 this.field.dom.value = d.activePage;
7860                 return;
7861             }
7862             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
7863             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7864             e.stopEvent();
7865         }
7866         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))
7867         {
7868           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
7869           this.field.dom.value = pageNum;
7870           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
7871           e.stopEvent();
7872         }
7873         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
7874         {
7875           var v = this.field.dom.value, pageNum; 
7876           var increment = (e.shiftKey) ? 10 : 1;
7877           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
7878             increment *= -1;
7879           }
7880           if(!v || isNaN(pageNum = parseInt(v, 10))) {
7881             this.field.dom.value = d.activePage;
7882             return;
7883           }
7884           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
7885           {
7886             this.field.dom.value = parseInt(v, 10) + increment;
7887             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
7888             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7889           }
7890           e.stopEvent();
7891         }
7892     },
7893
7894     // private
7895     beforeLoad : function(){
7896         if(this.loading){
7897             this.loading.disable();
7898         }
7899     },
7900
7901     // private
7902     onClick : function(which){
7903         var ds = this.ds;
7904         switch(which){
7905             case "first":
7906                 ds.load({params:{start: 0, limit: this.pageSize}});
7907             break;
7908             case "prev":
7909                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
7910             break;
7911             case "next":
7912                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
7913             break;
7914             case "last":
7915                 var total = ds.getTotalCount();
7916                 var extra = total % this.pageSize;
7917                 var lastStart = extra ? (total - extra) : total-this.pageSize;
7918                 ds.load({params:{start: lastStart, limit: this.pageSize}});
7919             break;
7920             case "refresh":
7921                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
7922             break;
7923         }
7924     },
7925
7926     /**
7927      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
7928      * @param {Roo.data.Store} store The data store to unbind
7929      */
7930     unbind : function(ds){
7931         ds.un("beforeload", this.beforeLoad, this);
7932         ds.un("load", this.onLoad, this);
7933         ds.un("loadexception", this.onLoadError, this);
7934         ds.un("remove", this.updateInfo, this);
7935         ds.un("add", this.updateInfo, this);
7936         this.ds = undefined;
7937     },
7938
7939     /**
7940      * Binds the paging toolbar to the specified {@link Roo.data.Store}
7941      * @param {Roo.data.Store} store The data store to bind
7942      */
7943     bind : function(ds){
7944         ds.on("beforeload", this.beforeLoad, this);
7945         ds.on("load", this.onLoad, this);
7946         ds.on("loadexception", this.onLoadError, this);
7947         ds.on("remove", this.updateInfo, this);
7948         ds.on("add", this.updateInfo, this);
7949         this.ds = ds;
7950     }
7951 });/*
7952  * Based on:
7953  * Ext JS Library 1.1.1
7954  * Copyright(c) 2006-2007, Ext JS, LLC.
7955  *
7956  * Originally Released Under LGPL - original licence link has changed is not relivant.
7957  *
7958  * Fork - LGPL
7959  * <script type="text/javascript">
7960  */
7961
7962 /**
7963  * @class Roo.Resizable
7964  * @extends Roo.util.Observable
7965  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
7966  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
7967  * 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
7968  * the element will be wrapped for you automatically.</p>
7969  * <p>Here is the list of valid resize handles:</p>
7970  * <pre>
7971 Value   Description
7972 ------  -------------------
7973  'n'     north
7974  's'     south
7975  'e'     east
7976  'w'     west
7977  'nw'    northwest
7978  'sw'    southwest
7979  'se'    southeast
7980  'ne'    northeast
7981  'hd'    horizontal drag
7982  'all'   all
7983 </pre>
7984  * <p>Here's an example showing the creation of a typical Resizable:</p>
7985  * <pre><code>
7986 var resizer = new Roo.Resizable("element-id", {
7987     handles: 'all',
7988     minWidth: 200,
7989     minHeight: 100,
7990     maxWidth: 500,
7991     maxHeight: 400,
7992     pinned: true
7993 });
7994 resizer.on("resize", myHandler);
7995 </code></pre>
7996  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
7997  * resizer.east.setDisplayed(false);</p>
7998  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
7999  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8000  * resize operation's new size (defaults to [0, 0])
8001  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8002  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8003  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8004  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8005  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8006  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8007  * @cfg {Number} width The width of the element in pixels (defaults to null)
8008  * @cfg {Number} height The height of the element in pixels (defaults to null)
8009  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8010  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8011  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8012  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8013  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8014  * in favor of the handles config option (defaults to false)
8015  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8016  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8017  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8018  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8019  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8020  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8021  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8022  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8023  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8024  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8025  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8026  * @constructor
8027  * Create a new resizable component
8028  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8029  * @param {Object} config configuration options
8030   */
8031 Roo.Resizable = function(el, config)
8032 {
8033     this.el = Roo.get(el);
8034
8035     if(config && config.wrap){
8036         config.resizeChild = this.el;
8037         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8038         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8039         this.el.setStyle("overflow", "hidden");
8040         this.el.setPositioning(config.resizeChild.getPositioning());
8041         config.resizeChild.clearPositioning();
8042         if(!config.width || !config.height){
8043             var csize = config.resizeChild.getSize();
8044             this.el.setSize(csize.width, csize.height);
8045         }
8046         if(config.pinned && !config.adjustments){
8047             config.adjustments = "auto";
8048         }
8049     }
8050
8051     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8052     this.proxy.unselectable();
8053     this.proxy.enableDisplayMode('block');
8054
8055     Roo.apply(this, config);
8056
8057     if(this.pinned){
8058         this.disableTrackOver = true;
8059         this.el.addClass("x-resizable-pinned");
8060     }
8061     // if the element isn't positioned, make it relative
8062     var position = this.el.getStyle("position");
8063     if(position != "absolute" && position != "fixed"){
8064         this.el.setStyle("position", "relative");
8065     }
8066     if(!this.handles){ // no handles passed, must be legacy style
8067         this.handles = 's,e,se';
8068         if(this.multiDirectional){
8069             this.handles += ',n,w';
8070         }
8071     }
8072     if(this.handles == "all"){
8073         this.handles = "n s e w ne nw se sw";
8074     }
8075     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8076     var ps = Roo.Resizable.positions;
8077     for(var i = 0, len = hs.length; i < len; i++){
8078         if(hs[i] && ps[hs[i]]){
8079             var pos = ps[hs[i]];
8080             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8081         }
8082     }
8083     // legacy
8084     this.corner = this.southeast;
8085     
8086     // updateBox = the box can move..
8087     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8088         this.updateBox = true;
8089     }
8090
8091     this.activeHandle = null;
8092
8093     if(this.resizeChild){
8094         if(typeof this.resizeChild == "boolean"){
8095             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8096         }else{
8097             this.resizeChild = Roo.get(this.resizeChild, true);
8098         }
8099     }
8100     
8101     if(this.adjustments == "auto"){
8102         var rc = this.resizeChild;
8103         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8104         if(rc && (hw || hn)){
8105             rc.position("relative");
8106             rc.setLeft(hw ? hw.el.getWidth() : 0);
8107             rc.setTop(hn ? hn.el.getHeight() : 0);
8108         }
8109         this.adjustments = [
8110             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8111             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8112         ];
8113     }
8114
8115     if(this.draggable){
8116         this.dd = this.dynamic ?
8117             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8118         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8119     }
8120
8121     // public events
8122     this.addEvents({
8123         /**
8124          * @event beforeresize
8125          * Fired before resize is allowed. Set enabled to false to cancel resize.
8126          * @param {Roo.Resizable} this
8127          * @param {Roo.EventObject} e The mousedown event
8128          */
8129         "beforeresize" : true,
8130         /**
8131          * @event resizing
8132          * Fired a resizing.
8133          * @param {Roo.Resizable} this
8134          * @param {Number} x The new x position
8135          * @param {Number} y The new y position
8136          * @param {Number} w The new w width
8137          * @param {Number} h The new h hight
8138          * @param {Roo.EventObject} e The mouseup event
8139          */
8140         "resizing" : true,
8141         /**
8142          * @event resize
8143          * Fired after a resize.
8144          * @param {Roo.Resizable} this
8145          * @param {Number} width The new width
8146          * @param {Number} height The new height
8147          * @param {Roo.EventObject} e The mouseup event
8148          */
8149         "resize" : true
8150     });
8151
8152     if(this.width !== null && this.height !== null){
8153         this.resizeTo(this.width, this.height);
8154     }else{
8155         this.updateChildSize();
8156     }
8157     if(Roo.isIE){
8158         this.el.dom.style.zoom = 1;
8159     }
8160     Roo.Resizable.superclass.constructor.call(this);
8161 };
8162
8163 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8164         resizeChild : false,
8165         adjustments : [0, 0],
8166         minWidth : 5,
8167         minHeight : 5,
8168         maxWidth : 10000,
8169         maxHeight : 10000,
8170         enabled : true,
8171         animate : false,
8172         duration : .35,
8173         dynamic : false,
8174         handles : false,
8175         multiDirectional : false,
8176         disableTrackOver : false,
8177         easing : 'easeOutStrong',
8178         widthIncrement : 0,
8179         heightIncrement : 0,
8180         pinned : false,
8181         width : null,
8182         height : null,
8183         preserveRatio : false,
8184         transparent: false,
8185         minX: 0,
8186         minY: 0,
8187         draggable: false,
8188
8189         /**
8190          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8191          */
8192         constrainTo: undefined,
8193         /**
8194          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8195          */
8196         resizeRegion: undefined,
8197
8198
8199     /**
8200      * Perform a manual resize
8201      * @param {Number} width
8202      * @param {Number} height
8203      */
8204     resizeTo : function(width, height){
8205         this.el.setSize(width, height);
8206         this.updateChildSize();
8207         this.fireEvent("resize", this, width, height, null);
8208     },
8209
8210     // private
8211     startSizing : function(e, handle){
8212         this.fireEvent("beforeresize", this, e);
8213         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8214
8215             if(!this.overlay){
8216                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8217                 this.overlay.unselectable();
8218                 this.overlay.enableDisplayMode("block");
8219                 this.overlay.on("mousemove", this.onMouseMove, this);
8220                 this.overlay.on("mouseup", this.onMouseUp, this);
8221             }
8222             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8223
8224             this.resizing = true;
8225             this.startBox = this.el.getBox();
8226             this.startPoint = e.getXY();
8227             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8228                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8229
8230             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8231             this.overlay.show();
8232
8233             if(this.constrainTo) {
8234                 var ct = Roo.get(this.constrainTo);
8235                 this.resizeRegion = ct.getRegion().adjust(
8236                     ct.getFrameWidth('t'),
8237                     ct.getFrameWidth('l'),
8238                     -ct.getFrameWidth('b'),
8239                     -ct.getFrameWidth('r')
8240                 );
8241             }
8242
8243             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8244             this.proxy.show();
8245             this.proxy.setBox(this.startBox);
8246             if(!this.dynamic){
8247                 this.proxy.setStyle('visibility', 'visible');
8248             }
8249         }
8250     },
8251
8252     // private
8253     onMouseDown : function(handle, e){
8254         if(this.enabled){
8255             e.stopEvent();
8256             this.activeHandle = handle;
8257             this.startSizing(e, handle);
8258         }
8259     },
8260
8261     // private
8262     onMouseUp : function(e){
8263         var size = this.resizeElement();
8264         this.resizing = false;
8265         this.handleOut();
8266         this.overlay.hide();
8267         this.proxy.hide();
8268         this.fireEvent("resize", this, size.width, size.height, e);
8269     },
8270
8271     // private
8272     updateChildSize : function(){
8273         
8274         if(this.resizeChild){
8275             var el = this.el;
8276             var child = this.resizeChild;
8277             var adj = this.adjustments;
8278             if(el.dom.offsetWidth){
8279                 var b = el.getSize(true);
8280                 child.setSize(b.width+adj[0], b.height+adj[1]);
8281             }
8282             // Second call here for IE
8283             // The first call enables instant resizing and
8284             // the second call corrects scroll bars if they
8285             // exist
8286             if(Roo.isIE){
8287                 setTimeout(function(){
8288                     if(el.dom.offsetWidth){
8289                         var b = el.getSize(true);
8290                         child.setSize(b.width+adj[0], b.height+adj[1]);
8291                     }
8292                 }, 10);
8293             }
8294         }
8295     },
8296
8297     // private
8298     snap : function(value, inc, min){
8299         if(!inc || !value) {
8300             return value;
8301         }
8302         var newValue = value;
8303         var m = value % inc;
8304         if(m > 0){
8305             if(m > (inc/2)){
8306                 newValue = value + (inc-m);
8307             }else{
8308                 newValue = value - m;
8309             }
8310         }
8311         return Math.max(min, newValue);
8312     },
8313
8314     // private
8315     resizeElement : function(){
8316         var box = this.proxy.getBox();
8317         if(this.updateBox){
8318             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8319         }else{
8320             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8321         }
8322         this.updateChildSize();
8323         if(!this.dynamic){
8324             this.proxy.hide();
8325         }
8326         return box;
8327     },
8328
8329     // private
8330     constrain : function(v, diff, m, mx){
8331         if(v - diff < m){
8332             diff = v - m;
8333         }else if(v - diff > mx){
8334             diff = mx - v;
8335         }
8336         return diff;
8337     },
8338
8339     // private
8340     onMouseMove : function(e){
8341         
8342         if(this.enabled){
8343             try{// try catch so if something goes wrong the user doesn't get hung
8344
8345             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8346                 return;
8347             }
8348
8349             //var curXY = this.startPoint;
8350             var curSize = this.curSize || this.startBox;
8351             var x = this.startBox.x, y = this.startBox.y;
8352             var ox = x, oy = y;
8353             var w = curSize.width, h = curSize.height;
8354             var ow = w, oh = h;
8355             var mw = this.minWidth, mh = this.minHeight;
8356             var mxw = this.maxWidth, mxh = this.maxHeight;
8357             var wi = this.widthIncrement;
8358             var hi = this.heightIncrement;
8359
8360             var eventXY = e.getXY();
8361             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8362             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8363
8364             var pos = this.activeHandle.position;
8365
8366             switch(pos){
8367                 case "east":
8368                     w += diffX;
8369                     w = Math.min(Math.max(mw, w), mxw);
8370                     break;
8371              
8372                 case "south":
8373                     h += diffY;
8374                     h = Math.min(Math.max(mh, h), mxh);
8375                     break;
8376                 case "southeast":
8377                     w += diffX;
8378                     h += diffY;
8379                     w = Math.min(Math.max(mw, w), mxw);
8380                     h = Math.min(Math.max(mh, h), mxh);
8381                     break;
8382                 case "north":
8383                     diffY = this.constrain(h, diffY, mh, mxh);
8384                     y += diffY;
8385                     h -= diffY;
8386                     break;
8387                 case "hdrag":
8388                     
8389                     if (wi) {
8390                         var adiffX = Math.abs(diffX);
8391                         var sub = (adiffX % wi); // how much 
8392                         if (sub > (wi/2)) { // far enough to snap
8393                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8394                         } else {
8395                             // remove difference.. 
8396                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8397                         }
8398                     }
8399                     x += diffX;
8400                     x = Math.max(this.minX, x);
8401                     break;
8402                 case "west":
8403                     diffX = this.constrain(w, diffX, mw, mxw);
8404                     x += diffX;
8405                     w -= diffX;
8406                     break;
8407                 case "northeast":
8408                     w += diffX;
8409                     w = Math.min(Math.max(mw, w), mxw);
8410                     diffY = this.constrain(h, diffY, mh, mxh);
8411                     y += diffY;
8412                     h -= diffY;
8413                     break;
8414                 case "northwest":
8415                     diffX = this.constrain(w, diffX, mw, mxw);
8416                     diffY = this.constrain(h, diffY, mh, mxh);
8417                     y += diffY;
8418                     h -= diffY;
8419                     x += diffX;
8420                     w -= diffX;
8421                     break;
8422                case "southwest":
8423                     diffX = this.constrain(w, diffX, mw, mxw);
8424                     h += diffY;
8425                     h = Math.min(Math.max(mh, h), mxh);
8426                     x += diffX;
8427                     w -= diffX;
8428                     break;
8429             }
8430
8431             var sw = this.snap(w, wi, mw);
8432             var sh = this.snap(h, hi, mh);
8433             if(sw != w || sh != h){
8434                 switch(pos){
8435                     case "northeast":
8436                         y -= sh - h;
8437                     break;
8438                     case "north":
8439                         y -= sh - h;
8440                         break;
8441                     case "southwest":
8442                         x -= sw - w;
8443                     break;
8444                     case "west":
8445                         x -= sw - w;
8446                         break;
8447                     case "northwest":
8448                         x -= sw - w;
8449                         y -= sh - h;
8450                     break;
8451                 }
8452                 w = sw;
8453                 h = sh;
8454             }
8455
8456             if(this.preserveRatio){
8457                 switch(pos){
8458                     case "southeast":
8459                     case "east":
8460                         h = oh * (w/ow);
8461                         h = Math.min(Math.max(mh, h), mxh);
8462                         w = ow * (h/oh);
8463                        break;
8464                     case "south":
8465                         w = ow * (h/oh);
8466                         w = Math.min(Math.max(mw, w), mxw);
8467                         h = oh * (w/ow);
8468                         break;
8469                     case "northeast":
8470                         w = ow * (h/oh);
8471                         w = Math.min(Math.max(mw, w), mxw);
8472                         h = oh * (w/ow);
8473                     break;
8474                     case "north":
8475                         var tw = w;
8476                         w = ow * (h/oh);
8477                         w = Math.min(Math.max(mw, w), mxw);
8478                         h = oh * (w/ow);
8479                         x += (tw - w) / 2;
8480                         break;
8481                     case "southwest":
8482                         h = oh * (w/ow);
8483                         h = Math.min(Math.max(mh, h), mxh);
8484                         var tw = w;
8485                         w = ow * (h/oh);
8486                         x += tw - w;
8487                         break;
8488                     case "west":
8489                         var th = h;
8490                         h = oh * (w/ow);
8491                         h = Math.min(Math.max(mh, h), mxh);
8492                         y += (th - h) / 2;
8493                         var tw = w;
8494                         w = ow * (h/oh);
8495                         x += tw - w;
8496                        break;
8497                     case "northwest":
8498                         var tw = w;
8499                         var th = h;
8500                         h = oh * (w/ow);
8501                         h = Math.min(Math.max(mh, h), mxh);
8502                         w = ow * (h/oh);
8503                         y += th - h;
8504                         x += tw - w;
8505                        break;
8506
8507                 }
8508             }
8509             if (pos == 'hdrag') {
8510                 w = ow;
8511             }
8512             this.proxy.setBounds(x, y, w, h);
8513             if(this.dynamic){
8514                 this.resizeElement();
8515             }
8516             }catch(e){}
8517         }
8518         this.fireEvent("resizing", this, x, y, w, h, e);
8519     },
8520
8521     // private
8522     handleOver : function(){
8523         if(this.enabled){
8524             this.el.addClass("x-resizable-over");
8525         }
8526     },
8527
8528     // private
8529     handleOut : function(){
8530         if(!this.resizing){
8531             this.el.removeClass("x-resizable-over");
8532         }
8533     },
8534
8535     /**
8536      * Returns the element this component is bound to.
8537      * @return {Roo.Element}
8538      */
8539     getEl : function(){
8540         return this.el;
8541     },
8542
8543     /**
8544      * Returns the resizeChild element (or null).
8545      * @return {Roo.Element}
8546      */
8547     getResizeChild : function(){
8548         return this.resizeChild;
8549     },
8550     groupHandler : function()
8551     {
8552         
8553     },
8554     /**
8555      * Destroys this resizable. If the element was wrapped and
8556      * removeEl is not true then the element remains.
8557      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8558      */
8559     destroy : function(removeEl){
8560         this.proxy.remove();
8561         if(this.overlay){
8562             this.overlay.removeAllListeners();
8563             this.overlay.remove();
8564         }
8565         var ps = Roo.Resizable.positions;
8566         for(var k in ps){
8567             if(typeof ps[k] != "function" && this[ps[k]]){
8568                 var h = this[ps[k]];
8569                 h.el.removeAllListeners();
8570                 h.el.remove();
8571             }
8572         }
8573         if(removeEl){
8574             this.el.update("");
8575             this.el.remove();
8576         }
8577     }
8578 });
8579
8580 // private
8581 // hash to map config positions to true positions
8582 Roo.Resizable.positions = {
8583     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
8584     hd: "hdrag"
8585 };
8586
8587 // private
8588 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8589     if(!this.tpl){
8590         // only initialize the template if resizable is used
8591         var tpl = Roo.DomHelper.createTemplate(
8592             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8593         );
8594         tpl.compile();
8595         Roo.Resizable.Handle.prototype.tpl = tpl;
8596     }
8597     this.position = pos;
8598     this.rz = rz;
8599     // show north drag fro topdra
8600     var handlepos = pos == 'hdrag' ? 'north' : pos;
8601     
8602     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
8603     if (pos == 'hdrag') {
8604         this.el.setStyle('cursor', 'pointer');
8605     }
8606     this.el.unselectable();
8607     if(transparent){
8608         this.el.setOpacity(0);
8609     }
8610     this.el.on("mousedown", this.onMouseDown, this);
8611     if(!disableTrackOver){
8612         this.el.on("mouseover", this.onMouseOver, this);
8613         this.el.on("mouseout", this.onMouseOut, this);
8614     }
8615 };
8616
8617 // private
8618 Roo.Resizable.Handle.prototype = {
8619     afterResize : function(rz){
8620         Roo.log('after?');
8621         // do nothing
8622     },
8623     // private
8624     onMouseDown : function(e){
8625         this.rz.onMouseDown(this, e);
8626     },
8627     // private
8628     onMouseOver : function(e){
8629         this.rz.handleOver(this, e);
8630     },
8631     // private
8632     onMouseOut : function(e){
8633         this.rz.handleOut(this, e);
8634     }
8635 };/*
8636  * Based on:
8637  * Ext JS Library 1.1.1
8638  * Copyright(c) 2006-2007, Ext JS, LLC.
8639  *
8640  * Originally Released Under LGPL - original licence link has changed is not relivant.
8641  *
8642  * Fork - LGPL
8643  * <script type="text/javascript">
8644  */
8645
8646 /**
8647  * @class Roo.Editor
8648  * @extends Roo.Component
8649  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
8650  * @constructor
8651  * Create a new Editor
8652  * @param {Roo.form.Field} field The Field object (or descendant)
8653  * @param {Object} config The config object
8654  */
8655 Roo.Editor = function(field, config){
8656     Roo.Editor.superclass.constructor.call(this, config);
8657     this.field = field;
8658     this.addEvents({
8659         /**
8660              * @event beforestartedit
8661              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
8662              * false from the handler of this event.
8663              * @param {Editor} this
8664              * @param {Roo.Element} boundEl The underlying element bound to this editor
8665              * @param {Mixed} value The field value being set
8666              */
8667         "beforestartedit" : true,
8668         /**
8669              * @event startedit
8670              * Fires when this editor is displayed
8671              * @param {Roo.Element} boundEl The underlying element bound to this editor
8672              * @param {Mixed} value The starting field value
8673              */
8674         "startedit" : true,
8675         /**
8676              * @event beforecomplete
8677              * Fires after a change has been made to the field, but before the change is reflected in the underlying
8678              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
8679              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
8680              * event will not fire since no edit actually occurred.
8681              * @param {Editor} this
8682              * @param {Mixed} value The current field value
8683              * @param {Mixed} startValue The original field value
8684              */
8685         "beforecomplete" : true,
8686         /**
8687              * @event complete
8688              * Fires after editing is complete and any changed value has been written to the underlying field.
8689              * @param {Editor} this
8690              * @param {Mixed} value The current field value
8691              * @param {Mixed} startValue The original field value
8692              */
8693         "complete" : true,
8694         /**
8695          * @event specialkey
8696          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8697          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8698          * @param {Roo.form.Field} this
8699          * @param {Roo.EventObject} e The event object
8700          */
8701         "specialkey" : true
8702     });
8703 };
8704
8705 Roo.extend(Roo.Editor, Roo.Component, {
8706     /**
8707      * @cfg {Boolean/String} autosize
8708      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
8709      * or "height" to adopt the height only (defaults to false)
8710      */
8711     /**
8712      * @cfg {Boolean} revertInvalid
8713      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
8714      * validation fails (defaults to true)
8715      */
8716     /**
8717      * @cfg {Boolean} ignoreNoChange
8718      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
8719      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
8720      * will never be ignored.
8721      */
8722     /**
8723      * @cfg {Boolean} hideEl
8724      * False to keep the bound element visible while the editor is displayed (defaults to true)
8725      */
8726     /**
8727      * @cfg {Mixed} value
8728      * The data value of the underlying field (defaults to "")
8729      */
8730     value : "",
8731     /**
8732      * @cfg {String} alignment
8733      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
8734      */
8735     alignment: "c-c?",
8736     /**
8737      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
8738      * for bottom-right shadow (defaults to "frame")
8739      */
8740     shadow : "frame",
8741     /**
8742      * @cfg {Boolean} constrain True to constrain the editor to the viewport
8743      */
8744     constrain : false,
8745     /**
8746      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
8747      */
8748     completeOnEnter : false,
8749     /**
8750      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
8751      */
8752     cancelOnEsc : false,
8753     /**
8754      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
8755      */
8756     updateEl : false,
8757
8758     // private
8759     onRender : function(ct, position){
8760         this.el = new Roo.Layer({
8761             shadow: this.shadow,
8762             cls: "x-editor",
8763             parentEl : ct,
8764             shim : this.shim,
8765             shadowOffset:4,
8766             id: this.id,
8767             constrain: this.constrain
8768         });
8769         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
8770         if(this.field.msgTarget != 'title'){
8771             this.field.msgTarget = 'qtip';
8772         }
8773         this.field.render(this.el);
8774         if(Roo.isGecko){
8775             this.field.el.dom.setAttribute('autocomplete', 'off');
8776         }
8777         this.field.on("specialkey", this.onSpecialKey, this);
8778         if(this.swallowKeys){
8779             this.field.el.swallowEvent(['keydown','keypress']);
8780         }
8781         this.field.show();
8782         this.field.on("blur", this.onBlur, this);
8783         if(this.field.grow){
8784             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
8785         }
8786     },
8787
8788     onSpecialKey : function(field, e)
8789     {
8790         //Roo.log('editor onSpecialKey');
8791         if(this.completeOnEnter && e.getKey() == e.ENTER){
8792             e.stopEvent();
8793             this.completeEdit();
8794             return;
8795         }
8796         // do not fire special key otherwise it might hide close the editor...
8797         if(e.getKey() == e.ENTER){    
8798             return;
8799         }
8800         if(this.cancelOnEsc && e.getKey() == e.ESC){
8801             this.cancelEdit();
8802             return;
8803         } 
8804         this.fireEvent('specialkey', field, e);
8805     
8806     },
8807
8808     /**
8809      * Starts the editing process and shows the editor.
8810      * @param {String/HTMLElement/Element} el The element to edit
8811      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
8812       * to the innerHTML of el.
8813      */
8814     startEdit : function(el, value){
8815         if(this.editing){
8816             this.completeEdit();
8817         }
8818         this.boundEl = Roo.get(el);
8819         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
8820         if(!this.rendered){
8821             this.render(this.parentEl || document.body);
8822         }
8823         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
8824             return;
8825         }
8826         this.startValue = v;
8827         this.field.setValue(v);
8828         if(this.autoSize){
8829             var sz = this.boundEl.getSize();
8830             switch(this.autoSize){
8831                 case "width":
8832                 this.setSize(sz.width,  "");
8833                 break;
8834                 case "height":
8835                 this.setSize("",  sz.height);
8836                 break;
8837                 default:
8838                 this.setSize(sz.width,  sz.height);
8839             }
8840         }
8841         this.el.alignTo(this.boundEl, this.alignment);
8842         this.editing = true;
8843         if(Roo.QuickTips){
8844             Roo.QuickTips.disable();
8845         }
8846         this.show();
8847     },
8848
8849     /**
8850      * Sets the height and width of this editor.
8851      * @param {Number} width The new width
8852      * @param {Number} height The new height
8853      */
8854     setSize : function(w, h){
8855         this.field.setSize(w, h);
8856         if(this.el){
8857             this.el.sync();
8858         }
8859     },
8860
8861     /**
8862      * Realigns the editor to the bound field based on the current alignment config value.
8863      */
8864     realign : function(){
8865         this.el.alignTo(this.boundEl, this.alignment);
8866     },
8867
8868     /**
8869      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
8870      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
8871      */
8872     completeEdit : function(remainVisible){
8873         if(!this.editing){
8874             return;
8875         }
8876         var v = this.getValue();
8877         if(this.revertInvalid !== false && !this.field.isValid()){
8878             v = this.startValue;
8879             this.cancelEdit(true);
8880         }
8881         if(String(v) === String(this.startValue) && this.ignoreNoChange){
8882             this.editing = false;
8883             this.hide();
8884             return;
8885         }
8886         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
8887             this.editing = false;
8888             if(this.updateEl && this.boundEl){
8889                 this.boundEl.update(v);
8890             }
8891             if(remainVisible !== true){
8892                 this.hide();
8893             }
8894             this.fireEvent("complete", this, v, this.startValue);
8895         }
8896     },
8897
8898     // private
8899     onShow : function(){
8900         this.el.show();
8901         if(this.hideEl !== false){
8902             this.boundEl.hide();
8903         }
8904         this.field.show();
8905         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
8906             this.fixIEFocus = true;
8907             this.deferredFocus.defer(50, this);
8908         }else{
8909             this.field.focus();
8910         }
8911         this.fireEvent("startedit", this.boundEl, this.startValue);
8912     },
8913
8914     deferredFocus : function(){
8915         if(this.editing){
8916             this.field.focus();
8917         }
8918     },
8919
8920     /**
8921      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
8922      * reverted to the original starting value.
8923      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
8924      * cancel (defaults to false)
8925      */
8926     cancelEdit : function(remainVisible){
8927         if(this.editing){
8928             this.setValue(this.startValue);
8929             if(remainVisible !== true){
8930                 this.hide();
8931             }
8932         }
8933     },
8934
8935     // private
8936     onBlur : function(){
8937         if(this.allowBlur !== true && this.editing){
8938             this.completeEdit();
8939         }
8940     },
8941
8942     // private
8943     onHide : function(){
8944         if(this.editing){
8945             this.completeEdit();
8946             return;
8947         }
8948         this.field.blur();
8949         if(this.field.collapse){
8950             this.field.collapse();
8951         }
8952         this.el.hide();
8953         if(this.hideEl !== false){
8954             this.boundEl.show();
8955         }
8956         if(Roo.QuickTips){
8957             Roo.QuickTips.enable();
8958         }
8959     },
8960
8961     /**
8962      * Sets the data value of the editor
8963      * @param {Mixed} value Any valid value supported by the underlying field
8964      */
8965     setValue : function(v){
8966         this.field.setValue(v);
8967     },
8968
8969     /**
8970      * Gets the data value of the editor
8971      * @return {Mixed} The data value
8972      */
8973     getValue : function(){
8974         return this.field.getValue();
8975     }
8976 });/*
8977  * Based on:
8978  * Ext JS Library 1.1.1
8979  * Copyright(c) 2006-2007, Ext JS, LLC.
8980  *
8981  * Originally Released Under LGPL - original licence link has changed is not relivant.
8982  *
8983  * Fork - LGPL
8984  * <script type="text/javascript">
8985  */
8986  
8987 /**
8988  * @class Roo.BasicDialog
8989  * @extends Roo.util.Observable
8990  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
8991  * <pre><code>
8992 var dlg = new Roo.BasicDialog("my-dlg", {
8993     height: 200,
8994     width: 300,
8995     minHeight: 100,
8996     minWidth: 150,
8997     modal: true,
8998     proxyDrag: true,
8999     shadow: true
9000 });
9001 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9002 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
9003 dlg.addButton('Cancel', dlg.hide, dlg);
9004 dlg.show();
9005 </code></pre>
9006   <b>A Dialog should always be a direct child of the body element.</b>
9007  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9008  * @cfg {String} title Default text to display in the title bar (defaults to null)
9009  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9010  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9011  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9012  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9013  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9014  * (defaults to null with no animation)
9015  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9016  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9017  * property for valid values (defaults to 'all')
9018  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9019  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9020  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9021  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9022  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9023  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9024  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9025  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9026  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9027  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9028  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9029  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9030  * draggable = true (defaults to false)
9031  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9032  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9033  * shadow (defaults to false)
9034  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9035  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9036  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9037  * @cfg {Array} buttons Array of buttons
9038  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9039  * @constructor
9040  * Create a new BasicDialog.
9041  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9042  * @param {Object} config Configuration options
9043  */
9044 Roo.BasicDialog = function(el, config){
9045     this.el = Roo.get(el);
9046     var dh = Roo.DomHelper;
9047     if(!this.el && config && config.autoCreate){
9048         if(typeof config.autoCreate == "object"){
9049             if(!config.autoCreate.id){
9050                 config.autoCreate.id = el;
9051             }
9052             this.el = dh.append(document.body,
9053                         config.autoCreate, true);
9054         }else{
9055             this.el = dh.append(document.body,
9056                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9057         }
9058     }
9059     el = this.el;
9060     el.setDisplayed(true);
9061     el.hide = this.hideAction;
9062     this.id = el.id;
9063     el.addClass("x-dlg");
9064
9065     Roo.apply(this, config);
9066
9067     this.proxy = el.createProxy("x-dlg-proxy");
9068     this.proxy.hide = this.hideAction;
9069     this.proxy.setOpacity(.5);
9070     this.proxy.hide();
9071
9072     if(config.width){
9073         el.setWidth(config.width);
9074     }
9075     if(config.height){
9076         el.setHeight(config.height);
9077     }
9078     this.size = el.getSize();
9079     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9080         this.xy = [config.x,config.y];
9081     }else{
9082         this.xy = el.getCenterXY(true);
9083     }
9084     /** The header element @type Roo.Element */
9085     this.header = el.child("> .x-dlg-hd");
9086     /** The body element @type Roo.Element */
9087     this.body = el.child("> .x-dlg-bd");
9088     /** The footer element @type Roo.Element */
9089     this.footer = el.child("> .x-dlg-ft");
9090
9091     if(!this.header){
9092         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9093     }
9094     if(!this.body){
9095         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9096     }
9097
9098     this.header.unselectable();
9099     if(this.title){
9100         this.header.update(this.title);
9101     }
9102     // this element allows the dialog to be focused for keyboard event
9103     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9104     this.focusEl.swallowEvent("click", true);
9105
9106     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9107
9108     // wrap the body and footer for special rendering
9109     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9110     if(this.footer){
9111         this.bwrap.dom.appendChild(this.footer.dom);
9112     }
9113
9114     this.bg = this.el.createChild({
9115         tag: "div", cls:"x-dlg-bg",
9116         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9117     });
9118     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9119
9120
9121     if(this.autoScroll !== false && !this.autoTabs){
9122         this.body.setStyle("overflow", "auto");
9123     }
9124
9125     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9126
9127     if(this.closable !== false){
9128         this.el.addClass("x-dlg-closable");
9129         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9130         this.close.on("click", this.closeClick, this);
9131         this.close.addClassOnOver("x-dlg-close-over");
9132     }
9133     if(this.collapsible !== false){
9134         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9135         this.collapseBtn.on("click", this.collapseClick, this);
9136         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9137         this.header.on("dblclick", this.collapseClick, this);
9138     }
9139     if(this.resizable !== false){
9140         this.el.addClass("x-dlg-resizable");
9141         this.resizer = new Roo.Resizable(el, {
9142             minWidth: this.minWidth || 80,
9143             minHeight:this.minHeight || 80,
9144             handles: this.resizeHandles || "all",
9145             pinned: true
9146         });
9147         this.resizer.on("beforeresize", this.beforeResize, this);
9148         this.resizer.on("resize", this.onResize, this);
9149     }
9150     if(this.draggable !== false){
9151         el.addClass("x-dlg-draggable");
9152         if (!this.proxyDrag) {
9153             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9154         }
9155         else {
9156             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9157         }
9158         dd.setHandleElId(this.header.id);
9159         dd.endDrag = this.endMove.createDelegate(this);
9160         dd.startDrag = this.startMove.createDelegate(this);
9161         dd.onDrag = this.onDrag.createDelegate(this);
9162         dd.scroll = false;
9163         this.dd = dd;
9164     }
9165     if(this.modal){
9166         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9167         this.mask.enableDisplayMode("block");
9168         this.mask.hide();
9169         this.el.addClass("x-dlg-modal");
9170     }
9171     if(this.shadow){
9172         this.shadow = new Roo.Shadow({
9173             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9174             offset : this.shadowOffset
9175         });
9176     }else{
9177         this.shadowOffset = 0;
9178     }
9179     if(Roo.useShims && this.shim !== false){
9180         this.shim = this.el.createShim();
9181         this.shim.hide = this.hideAction;
9182         this.shim.hide();
9183     }else{
9184         this.shim = false;
9185     }
9186     if(this.autoTabs){
9187         this.initTabs();
9188     }
9189     if (this.buttons) { 
9190         var bts= this.buttons;
9191         this.buttons = [];
9192         Roo.each(bts, function(b) {
9193             this.addButton(b);
9194         }, this);
9195     }
9196     
9197     
9198     this.addEvents({
9199         /**
9200          * @event keydown
9201          * Fires when a key is pressed
9202          * @param {Roo.BasicDialog} this
9203          * @param {Roo.EventObject} e
9204          */
9205         "keydown" : true,
9206         /**
9207          * @event move
9208          * Fires when this dialog is moved by the user.
9209          * @param {Roo.BasicDialog} this
9210          * @param {Number} x The new page X
9211          * @param {Number} y The new page Y
9212          */
9213         "move" : true,
9214         /**
9215          * @event resize
9216          * Fires when this dialog is resized by the user.
9217          * @param {Roo.BasicDialog} this
9218          * @param {Number} width The new width
9219          * @param {Number} height The new height
9220          */
9221         "resize" : true,
9222         /**
9223          * @event beforehide
9224          * Fires before this dialog is hidden.
9225          * @param {Roo.BasicDialog} this
9226          */
9227         "beforehide" : true,
9228         /**
9229          * @event hide
9230          * Fires when this dialog is hidden.
9231          * @param {Roo.BasicDialog} this
9232          */
9233         "hide" : true,
9234         /**
9235          * @event beforeshow
9236          * Fires before this dialog is shown.
9237          * @param {Roo.BasicDialog} this
9238          */
9239         "beforeshow" : true,
9240         /**
9241          * @event show
9242          * Fires when this dialog is shown.
9243          * @param {Roo.BasicDialog} this
9244          */
9245         "show" : true
9246     });
9247     el.on("keydown", this.onKeyDown, this);
9248     el.on("mousedown", this.toFront, this);
9249     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9250     this.el.hide();
9251     Roo.DialogManager.register(this);
9252     Roo.BasicDialog.superclass.constructor.call(this);
9253 };
9254
9255 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9256     shadowOffset: Roo.isIE ? 6 : 5,
9257     minHeight: 80,
9258     minWidth: 200,
9259     minButtonWidth: 75,
9260     defaultButton: null,
9261     buttonAlign: "right",
9262     tabTag: 'div',
9263     firstShow: true,
9264
9265     /**
9266      * Sets the dialog title text
9267      * @param {String} text The title text to display
9268      * @return {Roo.BasicDialog} this
9269      */
9270     setTitle : function(text){
9271         this.header.update(text);
9272         return this;
9273     },
9274
9275     // private
9276     closeClick : function(){
9277         this.hide();
9278     },
9279
9280     // private
9281     collapseClick : function(){
9282         this[this.collapsed ? "expand" : "collapse"]();
9283     },
9284
9285     /**
9286      * Collapses the dialog to its minimized state (only the title bar is visible).
9287      * Equivalent to the user clicking the collapse dialog button.
9288      */
9289     collapse : function(){
9290         if(!this.collapsed){
9291             this.collapsed = true;
9292             this.el.addClass("x-dlg-collapsed");
9293             this.restoreHeight = this.el.getHeight();
9294             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9295         }
9296     },
9297
9298     /**
9299      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9300      * clicking the expand dialog button.
9301      */
9302     expand : function(){
9303         if(this.collapsed){
9304             this.collapsed = false;
9305             this.el.removeClass("x-dlg-collapsed");
9306             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9307         }
9308     },
9309
9310     /**
9311      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9312      * @return {Roo.TabPanel} The tabs component
9313      */
9314     initTabs : function(){
9315         var tabs = this.getTabs();
9316         while(tabs.getTab(0)){
9317             tabs.removeTab(0);
9318         }
9319         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9320             var dom = el.dom;
9321             tabs.addTab(Roo.id(dom), dom.title);
9322             dom.title = "";
9323         });
9324         tabs.activate(0);
9325         return tabs;
9326     },
9327
9328     // private
9329     beforeResize : function(){
9330         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9331     },
9332
9333     // private
9334     onResize : function(){
9335         this.refreshSize();
9336         this.syncBodyHeight();
9337         this.adjustAssets();
9338         this.focus();
9339         this.fireEvent("resize", this, this.size.width, this.size.height);
9340     },
9341
9342     // private
9343     onKeyDown : function(e){
9344         if(this.isVisible()){
9345             this.fireEvent("keydown", this, e);
9346         }
9347     },
9348
9349     /**
9350      * Resizes the dialog.
9351      * @param {Number} width
9352      * @param {Number} height
9353      * @return {Roo.BasicDialog} this
9354      */
9355     resizeTo : function(width, height){
9356         this.el.setSize(width, height);
9357         this.size = {width: width, height: height};
9358         this.syncBodyHeight();
9359         if(this.fixedcenter){
9360             this.center();
9361         }
9362         if(this.isVisible()){
9363             this.constrainXY();
9364             this.adjustAssets();
9365         }
9366         this.fireEvent("resize", this, width, height);
9367         return this;
9368     },
9369
9370
9371     /**
9372      * Resizes the dialog to fit the specified content size.
9373      * @param {Number} width
9374      * @param {Number} height
9375      * @return {Roo.BasicDialog} this
9376      */
9377     setContentSize : function(w, h){
9378         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9379         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9380         //if(!this.el.isBorderBox()){
9381             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9382             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9383         //}
9384         if(this.tabs){
9385             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9386             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9387         }
9388         this.resizeTo(w, h);
9389         return this;
9390     },
9391
9392     /**
9393      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9394      * executed in response to a particular key being pressed while the dialog is active.
9395      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9396      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9397      * @param {Function} fn The function to call
9398      * @param {Object} scope (optional) The scope of the function
9399      * @return {Roo.BasicDialog} this
9400      */
9401     addKeyListener : function(key, fn, scope){
9402         var keyCode, shift, ctrl, alt;
9403         if(typeof key == "object" && !(key instanceof Array)){
9404             keyCode = key["key"];
9405             shift = key["shift"];
9406             ctrl = key["ctrl"];
9407             alt = key["alt"];
9408         }else{
9409             keyCode = key;
9410         }
9411         var handler = function(dlg, e){
9412             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9413                 var k = e.getKey();
9414                 if(keyCode instanceof Array){
9415                     for(var i = 0, len = keyCode.length; i < len; i++){
9416                         if(keyCode[i] == k){
9417                           fn.call(scope || window, dlg, k, e);
9418                           return;
9419                         }
9420                     }
9421                 }else{
9422                     if(k == keyCode){
9423                         fn.call(scope || window, dlg, k, e);
9424                     }
9425                 }
9426             }
9427         };
9428         this.on("keydown", handler);
9429         return this;
9430     },
9431
9432     /**
9433      * Returns the TabPanel component (creates it if it doesn't exist).
9434      * Note: If you wish to simply check for the existence of tabs without creating them,
9435      * check for a null 'tabs' property.
9436      * @return {Roo.TabPanel} The tabs component
9437      */
9438     getTabs : function(){
9439         if(!this.tabs){
9440             this.el.addClass("x-dlg-auto-tabs");
9441             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9442             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9443         }
9444         return this.tabs;
9445     },
9446
9447     /**
9448      * Adds a button to the footer section of the dialog.
9449      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9450      * object or a valid Roo.DomHelper element config
9451      * @param {Function} handler The function called when the button is clicked
9452      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9453      * @return {Roo.Button} The new button
9454      */
9455     addButton : function(config, handler, scope){
9456         var dh = Roo.DomHelper;
9457         if(!this.footer){
9458             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9459         }
9460         if(!this.btnContainer){
9461             var tb = this.footer.createChild({
9462
9463                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9464                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9465             }, null, true);
9466             this.btnContainer = tb.firstChild.firstChild.firstChild;
9467         }
9468         var bconfig = {
9469             handler: handler,
9470             scope: scope,
9471             minWidth: this.minButtonWidth,
9472             hideParent:true
9473         };
9474         if(typeof config == "string"){
9475             bconfig.text = config;
9476         }else{
9477             if(config.tag){
9478                 bconfig.dhconfig = config;
9479             }else{
9480                 Roo.apply(bconfig, config);
9481             }
9482         }
9483         var fc = false;
9484         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9485             bconfig.position = Math.max(0, bconfig.position);
9486             fc = this.btnContainer.childNodes[bconfig.position];
9487         }
9488          
9489         var btn = new Roo.Button(
9490             fc ? 
9491                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9492                 : this.btnContainer.appendChild(document.createElement("td")),
9493             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9494             bconfig
9495         );
9496         this.syncBodyHeight();
9497         if(!this.buttons){
9498             /**
9499              * Array of all the buttons that have been added to this dialog via addButton
9500              * @type Array
9501              */
9502             this.buttons = [];
9503         }
9504         this.buttons.push(btn);
9505         return btn;
9506     },
9507
9508     /**
9509      * Sets the default button to be focused when the dialog is displayed.
9510      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9511      * @return {Roo.BasicDialog} this
9512      */
9513     setDefaultButton : function(btn){
9514         this.defaultButton = btn;
9515         return this;
9516     },
9517
9518     // private
9519     getHeaderFooterHeight : function(safe){
9520         var height = 0;
9521         if(this.header){
9522            height += this.header.getHeight();
9523         }
9524         if(this.footer){
9525            var fm = this.footer.getMargins();
9526             height += (this.footer.getHeight()+fm.top+fm.bottom);
9527         }
9528         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9529         height += this.centerBg.getPadding("tb");
9530         return height;
9531     },
9532
9533     // private
9534     syncBodyHeight : function()
9535     {
9536         var bd = this.body, // the text
9537             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9538             bw = this.bwrap;
9539         var height = this.size.height - this.getHeaderFooterHeight(false);
9540         bd.setHeight(height-bd.getMargins("tb"));
9541         var hh = this.header.getHeight();
9542         var h = this.size.height-hh;
9543         cb.setHeight(h);
9544         
9545         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9546         bw.setHeight(h-cb.getPadding("tb"));
9547         
9548         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9549         bd.setWidth(bw.getWidth(true));
9550         if(this.tabs){
9551             this.tabs.syncHeight();
9552             if(Roo.isIE){
9553                 this.tabs.el.repaint();
9554             }
9555         }
9556     },
9557
9558     /**
9559      * Restores the previous state of the dialog if Roo.state is configured.
9560      * @return {Roo.BasicDialog} this
9561      */
9562     restoreState : function(){
9563         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9564         if(box && box.width){
9565             this.xy = [box.x, box.y];
9566             this.resizeTo(box.width, box.height);
9567         }
9568         return this;
9569     },
9570
9571     // private
9572     beforeShow : function(){
9573         this.expand();
9574         if(this.fixedcenter){
9575             this.xy = this.el.getCenterXY(true);
9576         }
9577         if(this.modal){
9578             Roo.get(document.body).addClass("x-body-masked");
9579             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9580             this.mask.show();
9581         }
9582         this.constrainXY();
9583     },
9584
9585     // private
9586     animShow : function(){
9587         var b = Roo.get(this.animateTarget).getBox();
9588         this.proxy.setSize(b.width, b.height);
9589         this.proxy.setLocation(b.x, b.y);
9590         this.proxy.show();
9591         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9592                     true, .35, this.showEl.createDelegate(this));
9593     },
9594
9595     /**
9596      * Shows the dialog.
9597      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
9598      * @return {Roo.BasicDialog} this
9599      */
9600     show : function(animateTarget){
9601         if (this.fireEvent("beforeshow", this) === false){
9602             return;
9603         }
9604         if(this.syncHeightBeforeShow){
9605             this.syncBodyHeight();
9606         }else if(this.firstShow){
9607             this.firstShow = false;
9608             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
9609         }
9610         this.animateTarget = animateTarget || this.animateTarget;
9611         if(!this.el.isVisible()){
9612             this.beforeShow();
9613             if(this.animateTarget && Roo.get(this.animateTarget)){
9614                 this.animShow();
9615             }else{
9616                 this.showEl();
9617             }
9618         }
9619         return this;
9620     },
9621
9622     // private
9623     showEl : function(){
9624         this.proxy.hide();
9625         this.el.setXY(this.xy);
9626         this.el.show();
9627         this.adjustAssets(true);
9628         this.toFront();
9629         this.focus();
9630         // IE peekaboo bug - fix found by Dave Fenwick
9631         if(Roo.isIE){
9632             this.el.repaint();
9633         }
9634         this.fireEvent("show", this);
9635     },
9636
9637     /**
9638      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
9639      * dialog itself will receive focus.
9640      */
9641     focus : function(){
9642         if(this.defaultButton){
9643             this.defaultButton.focus();
9644         }else{
9645             this.focusEl.focus();
9646         }
9647     },
9648
9649     // private
9650     constrainXY : function(){
9651         if(this.constraintoviewport !== false){
9652             if(!this.viewSize){
9653                 if(this.container){
9654                     var s = this.container.getSize();
9655                     this.viewSize = [s.width, s.height];
9656                 }else{
9657                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
9658                 }
9659             }
9660             var s = Roo.get(this.container||document).getScroll();
9661
9662             var x = this.xy[0], y = this.xy[1];
9663             var w = this.size.width, h = this.size.height;
9664             var vw = this.viewSize[0], vh = this.viewSize[1];
9665             // only move it if it needs it
9666             var moved = false;
9667             // first validate right/bottom
9668             if(x + w > vw+s.left){
9669                 x = vw - w;
9670                 moved = true;
9671             }
9672             if(y + h > vh+s.top){
9673                 y = vh - h;
9674                 moved = true;
9675             }
9676             // then make sure top/left isn't negative
9677             if(x < s.left){
9678                 x = s.left;
9679                 moved = true;
9680             }
9681             if(y < s.top){
9682                 y = s.top;
9683                 moved = true;
9684             }
9685             if(moved){
9686                 // cache xy
9687                 this.xy = [x, y];
9688                 if(this.isVisible()){
9689                     this.el.setLocation(x, y);
9690                     this.adjustAssets();
9691                 }
9692             }
9693         }
9694     },
9695
9696     // private
9697     onDrag : function(){
9698         if(!this.proxyDrag){
9699             this.xy = this.el.getXY();
9700             this.adjustAssets();
9701         }
9702     },
9703
9704     // private
9705     adjustAssets : function(doShow){
9706         var x = this.xy[0], y = this.xy[1];
9707         var w = this.size.width, h = this.size.height;
9708         if(doShow === true){
9709             if(this.shadow){
9710                 this.shadow.show(this.el);
9711             }
9712             if(this.shim){
9713                 this.shim.show();
9714             }
9715         }
9716         if(this.shadow && this.shadow.isVisible()){
9717             this.shadow.show(this.el);
9718         }
9719         if(this.shim && this.shim.isVisible()){
9720             this.shim.setBounds(x, y, w, h);
9721         }
9722     },
9723
9724     // private
9725     adjustViewport : function(w, h){
9726         if(!w || !h){
9727             w = Roo.lib.Dom.getViewWidth();
9728             h = Roo.lib.Dom.getViewHeight();
9729         }
9730         // cache the size
9731         this.viewSize = [w, h];
9732         if(this.modal && this.mask.isVisible()){
9733             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
9734             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9735         }
9736         if(this.isVisible()){
9737             this.constrainXY();
9738         }
9739     },
9740
9741     /**
9742      * Destroys this dialog and all its supporting elements (including any tabs, shim,
9743      * shadow, proxy, mask, etc.)  Also removes all event listeners.
9744      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
9745      */
9746     destroy : function(removeEl){
9747         if(this.isVisible()){
9748             this.animateTarget = null;
9749             this.hide();
9750         }
9751         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
9752         if(this.tabs){
9753             this.tabs.destroy(removeEl);
9754         }
9755         Roo.destroy(
9756              this.shim,
9757              this.proxy,
9758              this.resizer,
9759              this.close,
9760              this.mask
9761         );
9762         if(this.dd){
9763             this.dd.unreg();
9764         }
9765         if(this.buttons){
9766            for(var i = 0, len = this.buttons.length; i < len; i++){
9767                this.buttons[i].destroy();
9768            }
9769         }
9770         this.el.removeAllListeners();
9771         if(removeEl === true){
9772             this.el.update("");
9773             this.el.remove();
9774         }
9775         Roo.DialogManager.unregister(this);
9776     },
9777
9778     // private
9779     startMove : function(){
9780         if(this.proxyDrag){
9781             this.proxy.show();
9782         }
9783         if(this.constraintoviewport !== false){
9784             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
9785         }
9786     },
9787
9788     // private
9789     endMove : function(){
9790         if(!this.proxyDrag){
9791             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
9792         }else{
9793             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
9794             this.proxy.hide();
9795         }
9796         this.refreshSize();
9797         this.adjustAssets();
9798         this.focus();
9799         this.fireEvent("move", this, this.xy[0], this.xy[1]);
9800     },
9801
9802     /**
9803      * Brings this dialog to the front of any other visible dialogs
9804      * @return {Roo.BasicDialog} this
9805      */
9806     toFront : function(){
9807         Roo.DialogManager.bringToFront(this);
9808         return this;
9809     },
9810
9811     /**
9812      * Sends this dialog to the back (under) of any other visible dialogs
9813      * @return {Roo.BasicDialog} this
9814      */
9815     toBack : function(){
9816         Roo.DialogManager.sendToBack(this);
9817         return this;
9818     },
9819
9820     /**
9821      * Centers this dialog in the viewport
9822      * @return {Roo.BasicDialog} this
9823      */
9824     center : function(){
9825         var xy = this.el.getCenterXY(true);
9826         this.moveTo(xy[0], xy[1]);
9827         return this;
9828     },
9829
9830     /**
9831      * Moves the dialog's top-left corner to the specified point
9832      * @param {Number} x
9833      * @param {Number} y
9834      * @return {Roo.BasicDialog} this
9835      */
9836     moveTo : function(x, y){
9837         this.xy = [x,y];
9838         if(this.isVisible()){
9839             this.el.setXY(this.xy);
9840             this.adjustAssets();
9841         }
9842         return this;
9843     },
9844
9845     /**
9846      * Aligns the dialog to the specified element
9847      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9848      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
9849      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9850      * @return {Roo.BasicDialog} this
9851      */
9852     alignTo : function(element, position, offsets){
9853         this.xy = this.el.getAlignToXY(element, position, offsets);
9854         if(this.isVisible()){
9855             this.el.setXY(this.xy);
9856             this.adjustAssets();
9857         }
9858         return this;
9859     },
9860
9861     /**
9862      * Anchors an element to another element and realigns it when the window is resized.
9863      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9864      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
9865      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9866      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
9867      * is a number, it is used as the buffer delay (defaults to 50ms).
9868      * @return {Roo.BasicDialog} this
9869      */
9870     anchorTo : function(el, alignment, offsets, monitorScroll){
9871         var action = function(){
9872             this.alignTo(el, alignment, offsets);
9873         };
9874         Roo.EventManager.onWindowResize(action, this);
9875         var tm = typeof monitorScroll;
9876         if(tm != 'undefined'){
9877             Roo.EventManager.on(window, 'scroll', action, this,
9878                 {buffer: tm == 'number' ? monitorScroll : 50});
9879         }
9880         action.call(this);
9881         return this;
9882     },
9883
9884     /**
9885      * Returns true if the dialog is visible
9886      * @return {Boolean}
9887      */
9888     isVisible : function(){
9889         return this.el.isVisible();
9890     },
9891
9892     // private
9893     animHide : function(callback){
9894         var b = Roo.get(this.animateTarget).getBox();
9895         this.proxy.show();
9896         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
9897         this.el.hide();
9898         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
9899                     this.hideEl.createDelegate(this, [callback]));
9900     },
9901
9902     /**
9903      * Hides the dialog.
9904      * @param {Function} callback (optional) Function to call when the dialog is hidden
9905      * @return {Roo.BasicDialog} this
9906      */
9907     hide : function(callback){
9908         if (this.fireEvent("beforehide", this) === false){
9909             return;
9910         }
9911         if(this.shadow){
9912             this.shadow.hide();
9913         }
9914         if(this.shim) {
9915           this.shim.hide();
9916         }
9917         // sometimes animateTarget seems to get set.. causing problems...
9918         // this just double checks..
9919         if(this.animateTarget && Roo.get(this.animateTarget)) {
9920            this.animHide(callback);
9921         }else{
9922             this.el.hide();
9923             this.hideEl(callback);
9924         }
9925         return this;
9926     },
9927
9928     // private
9929     hideEl : function(callback){
9930         this.proxy.hide();
9931         if(this.modal){
9932             this.mask.hide();
9933             Roo.get(document.body).removeClass("x-body-masked");
9934         }
9935         this.fireEvent("hide", this);
9936         if(typeof callback == "function"){
9937             callback();
9938         }
9939     },
9940
9941     // private
9942     hideAction : function(){
9943         this.setLeft("-10000px");
9944         this.setTop("-10000px");
9945         this.setStyle("visibility", "hidden");
9946     },
9947
9948     // private
9949     refreshSize : function(){
9950         this.size = this.el.getSize();
9951         this.xy = this.el.getXY();
9952         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
9953     },
9954
9955     // private
9956     // z-index is managed by the DialogManager and may be overwritten at any time
9957     setZIndex : function(index){
9958         if(this.modal){
9959             this.mask.setStyle("z-index", index);
9960         }
9961         if(this.shim){
9962             this.shim.setStyle("z-index", ++index);
9963         }
9964         if(this.shadow){
9965             this.shadow.setZIndex(++index);
9966         }
9967         this.el.setStyle("z-index", ++index);
9968         if(this.proxy){
9969             this.proxy.setStyle("z-index", ++index);
9970         }
9971         if(this.resizer){
9972             this.resizer.proxy.setStyle("z-index", ++index);
9973         }
9974
9975         this.lastZIndex = index;
9976     },
9977
9978     /**
9979      * Returns the element for this dialog
9980      * @return {Roo.Element} The underlying dialog Element
9981      */
9982     getEl : function(){
9983         return this.el;
9984     }
9985 });
9986
9987 /**
9988  * @class Roo.DialogManager
9989  * Provides global access to BasicDialogs that have been created and
9990  * support for z-indexing (layering) multiple open dialogs.
9991  */
9992 Roo.DialogManager = function(){
9993     var list = {};
9994     var accessList = [];
9995     var front = null;
9996
9997     // private
9998     var sortDialogs = function(d1, d2){
9999         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10000     };
10001
10002     // private
10003     var orderDialogs = function(){
10004         accessList.sort(sortDialogs);
10005         var seed = Roo.DialogManager.zseed;
10006         for(var i = 0, len = accessList.length; i < len; i++){
10007             var dlg = accessList[i];
10008             if(dlg){
10009                 dlg.setZIndex(seed + (i*10));
10010             }
10011         }
10012     };
10013
10014     return {
10015         /**
10016          * The starting z-index for BasicDialogs (defaults to 9000)
10017          * @type Number The z-index value
10018          */
10019         zseed : 9000,
10020
10021         // private
10022         register : function(dlg){
10023             list[dlg.id] = dlg;
10024             accessList.push(dlg);
10025         },
10026
10027         // private
10028         unregister : function(dlg){
10029             delete list[dlg.id];
10030             var i=0;
10031             var len=0;
10032             if(!accessList.indexOf){
10033                 for(  i = 0, len = accessList.length; i < len; i++){
10034                     if(accessList[i] == dlg){
10035                         accessList.splice(i, 1);
10036                         return;
10037                     }
10038                 }
10039             }else{
10040                  i = accessList.indexOf(dlg);
10041                 if(i != -1){
10042                     accessList.splice(i, 1);
10043                 }
10044             }
10045         },
10046
10047         /**
10048          * Gets a registered dialog by id
10049          * @param {String/Object} id The id of the dialog or a dialog
10050          * @return {Roo.BasicDialog} this
10051          */
10052         get : function(id){
10053             return typeof id == "object" ? id : list[id];
10054         },
10055
10056         /**
10057          * Brings the specified dialog to the front
10058          * @param {String/Object} dlg The id of the dialog or a dialog
10059          * @return {Roo.BasicDialog} this
10060          */
10061         bringToFront : function(dlg){
10062             dlg = this.get(dlg);
10063             if(dlg != front){
10064                 front = dlg;
10065                 dlg._lastAccess = new Date().getTime();
10066                 orderDialogs();
10067             }
10068             return dlg;
10069         },
10070
10071         /**
10072          * Sends the specified dialog to the back
10073          * @param {String/Object} dlg The id of the dialog or a dialog
10074          * @return {Roo.BasicDialog} this
10075          */
10076         sendToBack : function(dlg){
10077             dlg = this.get(dlg);
10078             dlg._lastAccess = -(new Date().getTime());
10079             orderDialogs();
10080             return dlg;
10081         },
10082
10083         /**
10084          * Hides all dialogs
10085          */
10086         hideAll : function(){
10087             for(var id in list){
10088                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10089                     list[id].hide();
10090                 }
10091             }
10092         }
10093     };
10094 }();
10095
10096 /**
10097  * @class Roo.LayoutDialog
10098  * @extends Roo.BasicDialog
10099  * @children Roo.ContentPanel
10100  * @builder-top
10101  * Dialog which provides adjustments for working with a layout in a Dialog.
10102  * Add your necessary layout config options to the dialog's config.<br>
10103  * Example usage (including a nested layout):
10104  * <pre><code>
10105 if(!dialog){
10106     dialog = new Roo.LayoutDialog("download-dlg", {
10107         modal: true,
10108         width:600,
10109         height:450,
10110         shadow:true,
10111         minWidth:500,
10112         minHeight:350,
10113         autoTabs:true,
10114         proxyDrag:true,
10115         // layout config merges with the dialog config
10116         center:{
10117             tabPosition: "top",
10118             alwaysShowTabs: true
10119         }
10120     });
10121     dialog.addKeyListener(27, dialog.hide, dialog);
10122     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10123     dialog.addButton("Build It!", this.getDownload, this);
10124
10125     // we can even add nested layouts
10126     var innerLayout = new Roo.BorderLayout("dl-inner", {
10127         east: {
10128             initialSize: 200,
10129             autoScroll:true,
10130             split:true
10131         },
10132         center: {
10133             autoScroll:true
10134         }
10135     });
10136     innerLayout.beginUpdate();
10137     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10138     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10139     innerLayout.endUpdate(true);
10140
10141     var layout = dialog.getLayout();
10142     layout.beginUpdate();
10143     layout.add("center", new Roo.ContentPanel("standard-panel",
10144                         {title: "Download the Source", fitToFrame:true}));
10145     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10146                {title: "Build your own roo.js"}));
10147     layout.getRegion("center").showPanel(sp);
10148     layout.endUpdate();
10149 }
10150 </code></pre>
10151     * @constructor
10152     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10153     * @param {Object} config configuration options
10154   */
10155 Roo.LayoutDialog = function(el, cfg){
10156     
10157     var config=  cfg;
10158     if (typeof(cfg) == 'undefined') {
10159         config = Roo.apply({}, el);
10160         // not sure why we use documentElement here.. - it should always be body.
10161         // IE7 borks horribly if we use documentElement.
10162         // webkit also does not like documentElement - it creates a body element...
10163         el = Roo.get( document.body || document.documentElement ).createChild();
10164         //config.autoCreate = true;
10165     }
10166     
10167     
10168     config.autoTabs = false;
10169     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10170     this.body.setStyle({overflow:"hidden", position:"relative"});
10171     this.layout = new Roo.BorderLayout(this.body.dom, config);
10172     this.layout.monitorWindowResize = false;
10173     this.el.addClass("x-dlg-auto-layout");
10174     // fix case when center region overwrites center function
10175     this.center = Roo.BasicDialog.prototype.center;
10176     this.on("show", this.layout.layout, this.layout, true);
10177     if (config.items) {
10178         var xitems = config.items;
10179         delete config.items;
10180         Roo.each(xitems, this.addxtype, this);
10181     }
10182     
10183     
10184 };
10185 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10186     
10187     
10188     /**
10189      * @cfg {Roo.LayoutRegion} east  
10190      */
10191     /**
10192      * @cfg {Roo.LayoutRegion} west
10193      */
10194     /**
10195      * @cfg {Roo.LayoutRegion} south
10196      */
10197     /**
10198      * @cfg {Roo.LayoutRegion} north
10199      */
10200     /**
10201      * @cfg {Roo.LayoutRegion} center
10202      */
10203     /**
10204      * @cfg {Roo.Button} buttons[]  Bottom buttons..
10205      */
10206     
10207     
10208     /**
10209      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10210      * @deprecated
10211      */
10212     endUpdate : function(){
10213         this.layout.endUpdate();
10214     },
10215
10216     /**
10217      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10218      *  @deprecated
10219      */
10220     beginUpdate : function(){
10221         this.layout.beginUpdate();
10222     },
10223
10224     /**
10225      * Get the BorderLayout for this dialog
10226      * @return {Roo.BorderLayout}
10227      */
10228     getLayout : function(){
10229         return this.layout;
10230     },
10231
10232     showEl : function(){
10233         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10234         if(Roo.isIE7){
10235             this.layout.layout();
10236         }
10237     },
10238
10239     // private
10240     // Use the syncHeightBeforeShow config option to control this automatically
10241     syncBodyHeight : function(){
10242         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10243         if(this.layout){this.layout.layout();}
10244     },
10245     
10246       /**
10247      * Add an xtype element (actually adds to the layout.)
10248      * @return {Object} xdata xtype object data.
10249      */
10250     
10251     addxtype : function(c) {
10252         return this.layout.addxtype(c);
10253     }
10254 });/*
10255  * Based on:
10256  * Ext JS Library 1.1.1
10257  * Copyright(c) 2006-2007, Ext JS, LLC.
10258  *
10259  * Originally Released Under LGPL - original licence link has changed is not relivant.
10260  *
10261  * Fork - LGPL
10262  * <script type="text/javascript">
10263  */
10264  
10265 /**
10266  * @class Roo.MessageBox
10267  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10268  * Example usage:
10269  *<pre><code>
10270 // Basic alert:
10271 Roo.Msg.alert('Status', 'Changes saved successfully.');
10272
10273 // Prompt for user data:
10274 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10275     if (btn == 'ok'){
10276         // process text value...
10277     }
10278 });
10279
10280 // Show a dialog using config options:
10281 Roo.Msg.show({
10282    title:'Save Changes?',
10283    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10284    buttons: Roo.Msg.YESNOCANCEL,
10285    fn: processResult,
10286    animEl: 'elId'
10287 });
10288 </code></pre>
10289  * @singleton
10290  */
10291 Roo.MessageBox = function(){
10292     var dlg, opt, mask, waitTimer;
10293     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10294     var buttons, activeTextEl, bwidth;
10295
10296     // private
10297     var handleButton = function(button){
10298         dlg.hide();
10299         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10300     };
10301
10302     // private
10303     var handleHide = function(){
10304         if(opt && opt.cls){
10305             dlg.el.removeClass(opt.cls);
10306         }
10307         if(waitTimer){
10308             Roo.TaskMgr.stop(waitTimer);
10309             waitTimer = null;
10310         }
10311     };
10312
10313     // private
10314     var updateButtons = function(b){
10315         var width = 0;
10316         if(!b){
10317             buttons["ok"].hide();
10318             buttons["cancel"].hide();
10319             buttons["yes"].hide();
10320             buttons["no"].hide();
10321             dlg.footer.dom.style.display = 'none';
10322             return width;
10323         }
10324         dlg.footer.dom.style.display = '';
10325         for(var k in buttons){
10326             if(typeof buttons[k] != "function"){
10327                 if(b[k]){
10328                     buttons[k].show();
10329                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10330                     width += buttons[k].el.getWidth()+15;
10331                 }else{
10332                     buttons[k].hide();
10333                 }
10334             }
10335         }
10336         return width;
10337     };
10338
10339     // private
10340     var handleEsc = function(d, k, e){
10341         if(opt && opt.closable !== false){
10342             dlg.hide();
10343         }
10344         if(e){
10345             e.stopEvent();
10346         }
10347     };
10348
10349     return {
10350         /**
10351          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10352          * @return {Roo.BasicDialog} The BasicDialog element
10353          */
10354         getDialog : function(){
10355            if(!dlg){
10356                 dlg = new Roo.BasicDialog("x-msg-box", {
10357                     autoCreate : true,
10358                     shadow: true,
10359                     draggable: true,
10360                     resizable:false,
10361                     constraintoviewport:false,
10362                     fixedcenter:true,
10363                     collapsible : false,
10364                     shim:true,
10365                     modal: true,
10366                     width:400, height:100,
10367                     buttonAlign:"center",
10368                     closeClick : function(){
10369                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10370                             handleButton("no");
10371                         }else{
10372                             handleButton("cancel");
10373                         }
10374                     }
10375                 });
10376                 dlg.on("hide", handleHide);
10377                 mask = dlg.mask;
10378                 dlg.addKeyListener(27, handleEsc);
10379                 buttons = {};
10380                 var bt = this.buttonText;
10381                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10382                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10383                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10384                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10385                 bodyEl = dlg.body.createChild({
10386
10387                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
10388                 });
10389                 msgEl = bodyEl.dom.firstChild;
10390                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10391                 textboxEl.enableDisplayMode();
10392                 textboxEl.addKeyListener([10,13], function(){
10393                     if(dlg.isVisible() && opt && opt.buttons){
10394                         if(opt.buttons.ok){
10395                             handleButton("ok");
10396                         }else if(opt.buttons.yes){
10397                             handleButton("yes");
10398                         }
10399                     }
10400                 });
10401                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10402                 textareaEl.enableDisplayMode();
10403                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10404                 progressEl.enableDisplayMode();
10405                 var pf = progressEl.dom.firstChild;
10406                 if (pf) {
10407                     pp = Roo.get(pf.firstChild);
10408                     pp.setHeight(pf.offsetHeight);
10409                 }
10410                 
10411             }
10412             return dlg;
10413         },
10414
10415         /**
10416          * Updates the message box body text
10417          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10418          * the XHTML-compliant non-breaking space character '&amp;#160;')
10419          * @return {Roo.MessageBox} This message box
10420          */
10421         updateText : function(text){
10422             if(!dlg.isVisible() && !opt.width){
10423                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10424             }
10425             msgEl.innerHTML = text || '&#160;';
10426       
10427             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10428             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10429             var w = Math.max(
10430                     Math.min(opt.width || cw , this.maxWidth), 
10431                     Math.max(opt.minWidth || this.minWidth, bwidth)
10432             );
10433             if(opt.prompt){
10434                 activeTextEl.setWidth(w);
10435             }
10436             if(dlg.isVisible()){
10437                 dlg.fixedcenter = false;
10438             }
10439             // to big, make it scroll. = But as usual stupid IE does not support
10440             // !important..
10441             
10442             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10443                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10444                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10445             } else {
10446                 bodyEl.dom.style.height = '';
10447                 bodyEl.dom.style.overflowY = '';
10448             }
10449             if (cw > w) {
10450                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10451             } else {
10452                 bodyEl.dom.style.overflowX = '';
10453             }
10454             
10455             dlg.setContentSize(w, bodyEl.getHeight());
10456             if(dlg.isVisible()){
10457                 dlg.fixedcenter = true;
10458             }
10459             return this;
10460         },
10461
10462         /**
10463          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10464          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10465          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10466          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10467          * @return {Roo.MessageBox} This message box
10468          */
10469         updateProgress : function(value, text){
10470             if(text){
10471                 this.updateText(text);
10472             }
10473             if (pp) { // weird bug on my firefox - for some reason this is not defined
10474                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10475             }
10476             return this;
10477         },        
10478
10479         /**
10480          * Returns true if the message box is currently displayed
10481          * @return {Boolean} True if the message box is visible, else false
10482          */
10483         isVisible : function(){
10484             return dlg && dlg.isVisible();  
10485         },
10486
10487         /**
10488          * Hides the message box if it is displayed
10489          */
10490         hide : function(){
10491             if(this.isVisible()){
10492                 dlg.hide();
10493             }  
10494         },
10495
10496         /**
10497          * Displays a new message box, or reinitializes an existing message box, based on the config options
10498          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10499          * The following config object properties are supported:
10500          * <pre>
10501 Property    Type             Description
10502 ----------  ---------------  ------------------------------------------------------------------------------------
10503 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10504                                    closes (defaults to undefined)
10505 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10506                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10507 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10508                                    progress and wait dialogs will ignore this property and always hide the
10509                                    close button as they can only be closed programmatically.
10510 cls               String           A custom CSS class to apply to the message box element
10511 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10512                                    displayed (defaults to 75)
10513 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10514                                    function will be btn (the name of the button that was clicked, if applicable,
10515                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10516                                    Progress and wait dialogs will ignore this option since they do not respond to
10517                                    user actions and can only be closed programmatically, so any required function
10518                                    should be called by the same code after it closes the dialog.
10519 icon              String           A CSS class that provides a background image to be used as an icon for
10520                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10521 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10522 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10523 modal             Boolean          False to allow user interaction with the page while the message box is
10524                                    displayed (defaults to true)
10525 msg               String           A string that will replace the existing message box body text (defaults
10526                                    to the XHTML-compliant non-breaking space character '&#160;')
10527 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10528 progress          Boolean          True to display a progress bar (defaults to false)
10529 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10530 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10531 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10532 title             String           The title text
10533 value             String           The string value to set into the active textbox element if displayed
10534 wait              Boolean          True to display a progress bar (defaults to false)
10535 width             Number           The width of the dialog in pixels
10536 </pre>
10537          *
10538          * Example usage:
10539          * <pre><code>
10540 Roo.Msg.show({
10541    title: 'Address',
10542    msg: 'Please enter your address:',
10543    width: 300,
10544    buttons: Roo.MessageBox.OKCANCEL,
10545    multiline: true,
10546    fn: saveAddress,
10547    animEl: 'addAddressBtn'
10548 });
10549 </code></pre>
10550          * @param {Object} config Configuration options
10551          * @return {Roo.MessageBox} This message box
10552          */
10553         show : function(options)
10554         {
10555             
10556             // this causes nightmares if you show one dialog after another
10557             // especially on callbacks..
10558              
10559             if(this.isVisible()){
10560                 
10561                 this.hide();
10562                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10563                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10564                 Roo.log("New Dialog Message:" +  options.msg )
10565                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10566                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10567                 
10568             }
10569             var d = this.getDialog();
10570             opt = options;
10571             d.setTitle(opt.title || "&#160;");
10572             d.close.setDisplayed(opt.closable !== false);
10573             activeTextEl = textboxEl;
10574             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10575             if(opt.prompt){
10576                 if(opt.multiline){
10577                     textboxEl.hide();
10578                     textareaEl.show();
10579                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10580                         opt.multiline : this.defaultTextHeight);
10581                     activeTextEl = textareaEl;
10582                 }else{
10583                     textboxEl.show();
10584                     textareaEl.hide();
10585                 }
10586             }else{
10587                 textboxEl.hide();
10588                 textareaEl.hide();
10589             }
10590             progressEl.setDisplayed(opt.progress === true);
10591             this.updateProgress(0);
10592             activeTextEl.dom.value = opt.value || "";
10593             if(opt.prompt){
10594                 dlg.setDefaultButton(activeTextEl);
10595             }else{
10596                 var bs = opt.buttons;
10597                 var db = null;
10598                 if(bs && bs.ok){
10599                     db = buttons["ok"];
10600                 }else if(bs && bs.yes){
10601                     db = buttons["yes"];
10602                 }
10603                 dlg.setDefaultButton(db);
10604             }
10605             bwidth = updateButtons(opt.buttons);
10606             this.updateText(opt.msg);
10607             if(opt.cls){
10608                 d.el.addClass(opt.cls);
10609             }
10610             d.proxyDrag = opt.proxyDrag === true;
10611             d.modal = opt.modal !== false;
10612             d.mask = opt.modal !== false ? mask : false;
10613             if(!d.isVisible()){
10614                 // force it to the end of the z-index stack so it gets a cursor in FF
10615                 document.body.appendChild(dlg.el.dom);
10616                 d.animateTarget = null;
10617                 d.show(options.animEl);
10618             }
10619             return this;
10620         },
10621
10622         /**
10623          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
10624          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
10625          * and closing the message box when the process is complete.
10626          * @param {String} title The title bar text
10627          * @param {String} msg The message box body text
10628          * @return {Roo.MessageBox} This message box
10629          */
10630         progress : function(title, msg){
10631             this.show({
10632                 title : title,
10633                 msg : msg,
10634                 buttons: false,
10635                 progress:true,
10636                 closable:false,
10637                 minWidth: this.minProgressWidth,
10638                 modal : true
10639             });
10640             return this;
10641         },
10642
10643         /**
10644          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
10645          * If a callback function is passed it will be called after the user clicks the button, and the
10646          * id of the button that was clicked will be passed as the only parameter to the callback
10647          * (could also be the top-right close button).
10648          * @param {String} title The title bar text
10649          * @param {String} msg The message box body text
10650          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10651          * @param {Object} scope (optional) The scope of the callback function
10652          * @return {Roo.MessageBox} This message box
10653          */
10654         alert : function(title, msg, fn, scope){
10655             this.show({
10656                 title : title,
10657                 msg : msg,
10658                 buttons: this.OK,
10659                 fn: fn,
10660                 scope : scope,
10661                 modal : true
10662             });
10663             return this;
10664         },
10665
10666         /**
10667          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
10668          * interaction while waiting for a long-running process to complete that does not have defined intervals.
10669          * You are responsible for closing the message box when the process is complete.
10670          * @param {String} msg The message box body text
10671          * @param {String} title (optional) The title bar text
10672          * @return {Roo.MessageBox} This message box
10673          */
10674         wait : function(msg, title){
10675             this.show({
10676                 title : title,
10677                 msg : msg,
10678                 buttons: false,
10679                 closable:false,
10680                 progress:true,
10681                 modal:true,
10682                 width:300,
10683                 wait:true
10684             });
10685             waitTimer = Roo.TaskMgr.start({
10686                 run: function(i){
10687                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
10688                 },
10689                 interval: 1000
10690             });
10691             return this;
10692         },
10693
10694         /**
10695          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
10696          * If a callback function is passed it will be called after the user clicks either button, and the id of the
10697          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
10698          * @param {String} title The title bar text
10699          * @param {String} msg The message box body text
10700          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10701          * @param {Object} scope (optional) The scope of the callback function
10702          * @return {Roo.MessageBox} This message box
10703          */
10704         confirm : function(title, msg, fn, scope){
10705             this.show({
10706                 title : title,
10707                 msg : msg,
10708                 buttons: this.YESNO,
10709                 fn: fn,
10710                 scope : scope,
10711                 modal : true
10712             });
10713             return this;
10714         },
10715
10716         /**
10717          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
10718          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
10719          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
10720          * (could also be the top-right close button) and the text that was entered will be passed as the two
10721          * parameters to the callback.
10722          * @param {String} title The title bar text
10723          * @param {String} msg The message box body text
10724          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10725          * @param {Object} scope (optional) The scope of the callback function
10726          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
10727          * property, or the height in pixels to create the textbox (defaults to false / single-line)
10728          * @return {Roo.MessageBox} This message box
10729          */
10730         prompt : function(title, msg, fn, scope, multiline){
10731             this.show({
10732                 title : title,
10733                 msg : msg,
10734                 buttons: this.OKCANCEL,
10735                 fn: fn,
10736                 minWidth:250,
10737                 scope : scope,
10738                 prompt:true,
10739                 multiline: multiline,
10740                 modal : true
10741             });
10742             return this;
10743         },
10744
10745         /**
10746          * Button config that displays a single OK button
10747          * @type Object
10748          */
10749         OK : {ok:true},
10750         /**
10751          * Button config that displays Yes and No buttons
10752          * @type Object
10753          */
10754         YESNO : {yes:true, no:true},
10755         /**
10756          * Button config that displays OK and Cancel buttons
10757          * @type Object
10758          */
10759         OKCANCEL : {ok:true, cancel:true},
10760         /**
10761          * Button config that displays Yes, No and Cancel buttons
10762          * @type Object
10763          */
10764         YESNOCANCEL : {yes:true, no:true, cancel:true},
10765
10766         /**
10767          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
10768          * @type Number
10769          */
10770         defaultTextHeight : 75,
10771         /**
10772          * The maximum width in pixels of the message box (defaults to 600)
10773          * @type Number
10774          */
10775         maxWidth : 600,
10776         /**
10777          * The minimum width in pixels of the message box (defaults to 100)
10778          * @type Number
10779          */
10780         minWidth : 100,
10781         /**
10782          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
10783          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
10784          * @type Number
10785          */
10786         minProgressWidth : 250,
10787         /**
10788          * An object containing the default button text strings that can be overriden for localized language support.
10789          * Supported properties are: ok, cancel, yes and no.
10790          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
10791          * @type Object
10792          */
10793         buttonText : {
10794             ok : "OK",
10795             cancel : "Cancel",
10796             yes : "Yes",
10797             no : "No"
10798         }
10799     };
10800 }();
10801
10802 /**
10803  * Shorthand for {@link Roo.MessageBox}
10804  */
10805 Roo.Msg = Roo.MessageBox;/*
10806  * Based on:
10807  * Ext JS Library 1.1.1
10808  * Copyright(c) 2006-2007, Ext JS, LLC.
10809  *
10810  * Originally Released Under LGPL - original licence link has changed is not relivant.
10811  *
10812  * Fork - LGPL
10813  * <script type="text/javascript">
10814  */
10815 /**
10816  * @class Roo.QuickTips
10817  * Provides attractive and customizable tooltips for any element.
10818  * @singleton
10819  */
10820 Roo.QuickTips = function(){
10821     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
10822     var ce, bd, xy, dd;
10823     var visible = false, disabled = true, inited = false;
10824     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
10825     
10826     var onOver = function(e){
10827         if(disabled){
10828             return;
10829         }
10830         var t = e.getTarget();
10831         if(!t || t.nodeType !== 1 || t == document || t == document.body){
10832             return;
10833         }
10834         if(ce && t == ce.el){
10835             clearTimeout(hideProc);
10836             return;
10837         }
10838         if(t && tagEls[t.id]){
10839             tagEls[t.id].el = t;
10840             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
10841             return;
10842         }
10843         var ttp, et = Roo.fly(t);
10844         var ns = cfg.namespace;
10845         if(tm.interceptTitles && t.title){
10846             ttp = t.title;
10847             t.qtip = ttp;
10848             t.removeAttribute("title");
10849             e.preventDefault();
10850         }else{
10851             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
10852         }
10853         if(ttp){
10854             showProc = show.defer(tm.showDelay, tm, [{
10855                 el: t, 
10856                 text: ttp.replace(/\\n/g,'<br/>'),
10857                 width: et.getAttributeNS(ns, cfg.width),
10858                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
10859                 title: et.getAttributeNS(ns, cfg.title),
10860                     cls: et.getAttributeNS(ns, cfg.cls)
10861             }]);
10862         }
10863     };
10864     
10865     var onOut = function(e){
10866         clearTimeout(showProc);
10867         var t = e.getTarget();
10868         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
10869             hideProc = setTimeout(hide, tm.hideDelay);
10870         }
10871     };
10872     
10873     var onMove = function(e){
10874         if(disabled){
10875             return;
10876         }
10877         xy = e.getXY();
10878         xy[1] += 18;
10879         if(tm.trackMouse && ce){
10880             el.setXY(xy);
10881         }
10882     };
10883     
10884     var onDown = function(e){
10885         clearTimeout(showProc);
10886         clearTimeout(hideProc);
10887         if(!e.within(el)){
10888             if(tm.hideOnClick){
10889                 hide();
10890                 tm.disable();
10891                 tm.enable.defer(100, tm);
10892             }
10893         }
10894     };
10895     
10896     var getPad = function(){
10897         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
10898     };
10899
10900     var show = function(o){
10901         if(disabled){
10902             return;
10903         }
10904         clearTimeout(dismissProc);
10905         ce = o;
10906         if(removeCls){ // in case manually hidden
10907             el.removeClass(removeCls);
10908             removeCls = null;
10909         }
10910         if(ce.cls){
10911             el.addClass(ce.cls);
10912             removeCls = ce.cls;
10913         }
10914         if(ce.title){
10915             tipTitle.update(ce.title);
10916             tipTitle.show();
10917         }else{
10918             tipTitle.update('');
10919             tipTitle.hide();
10920         }
10921         el.dom.style.width  = tm.maxWidth+'px';
10922         //tipBody.dom.style.width = '';
10923         tipBodyText.update(o.text);
10924         var p = getPad(), w = ce.width;
10925         if(!w){
10926             var td = tipBodyText.dom;
10927             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
10928             if(aw > tm.maxWidth){
10929                 w = tm.maxWidth;
10930             }else if(aw < tm.minWidth){
10931                 w = tm.minWidth;
10932             }else{
10933                 w = aw;
10934             }
10935         }
10936         //tipBody.setWidth(w);
10937         el.setWidth(parseInt(w, 10) + p);
10938         if(ce.autoHide === false){
10939             close.setDisplayed(true);
10940             if(dd){
10941                 dd.unlock();
10942             }
10943         }else{
10944             close.setDisplayed(false);
10945             if(dd){
10946                 dd.lock();
10947             }
10948         }
10949         if(xy){
10950             el.avoidY = xy[1]-18;
10951             el.setXY(xy);
10952         }
10953         if(tm.animate){
10954             el.setOpacity(.1);
10955             el.setStyle("visibility", "visible");
10956             el.fadeIn({callback: afterShow});
10957         }else{
10958             afterShow();
10959         }
10960     };
10961     
10962     var afterShow = function(){
10963         if(ce){
10964             el.show();
10965             esc.enable();
10966             if(tm.autoDismiss && ce.autoHide !== false){
10967                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
10968             }
10969         }
10970     };
10971     
10972     var hide = function(noanim){
10973         clearTimeout(dismissProc);
10974         clearTimeout(hideProc);
10975         ce = null;
10976         if(el.isVisible()){
10977             esc.disable();
10978             if(noanim !== true && tm.animate){
10979                 el.fadeOut({callback: afterHide});
10980             }else{
10981                 afterHide();
10982             } 
10983         }
10984     };
10985     
10986     var afterHide = function(){
10987         el.hide();
10988         if(removeCls){
10989             el.removeClass(removeCls);
10990             removeCls = null;
10991         }
10992     };
10993     
10994     return {
10995         /**
10996         * @cfg {Number} minWidth
10997         * The minimum width of the quick tip (defaults to 40)
10998         */
10999        minWidth : 40,
11000         /**
11001         * @cfg {Number} maxWidth
11002         * The maximum width of the quick tip (defaults to 300)
11003         */
11004        maxWidth : 300,
11005         /**
11006         * @cfg {Boolean} interceptTitles
11007         * True to automatically use the element's DOM title value if available (defaults to false)
11008         */
11009        interceptTitles : false,
11010         /**
11011         * @cfg {Boolean} trackMouse
11012         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11013         */
11014        trackMouse : false,
11015         /**
11016         * @cfg {Boolean} hideOnClick
11017         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11018         */
11019        hideOnClick : true,
11020         /**
11021         * @cfg {Number} showDelay
11022         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11023         */
11024        showDelay : 500,
11025         /**
11026         * @cfg {Number} hideDelay
11027         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11028         */
11029        hideDelay : 200,
11030         /**
11031         * @cfg {Boolean} autoHide
11032         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11033         * Used in conjunction with hideDelay.
11034         */
11035        autoHide : true,
11036         /**
11037         * @cfg {Boolean}
11038         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11039         * (defaults to true).  Used in conjunction with autoDismissDelay.
11040         */
11041        autoDismiss : true,
11042         /**
11043         * @cfg {Number}
11044         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11045         */
11046        autoDismissDelay : 5000,
11047        /**
11048         * @cfg {Boolean} animate
11049         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11050         */
11051        animate : false,
11052
11053        /**
11054         * @cfg {String} title
11055         * Title text to display (defaults to '').  This can be any valid HTML markup.
11056         */
11057         title: '',
11058        /**
11059         * @cfg {String} text
11060         * Body text to display (defaults to '').  This can be any valid HTML markup.
11061         */
11062         text : '',
11063        /**
11064         * @cfg {String} cls
11065         * A CSS class to apply to the base quick tip element (defaults to '').
11066         */
11067         cls : '',
11068        /**
11069         * @cfg {Number} width
11070         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11071         * minWidth or maxWidth.
11072         */
11073         width : null,
11074
11075     /**
11076      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11077      * or display QuickTips in a page.
11078      */
11079        init : function(){
11080           tm = Roo.QuickTips;
11081           cfg = tm.tagConfig;
11082           if(!inited){
11083               if(!Roo.isReady){ // allow calling of init() before onReady
11084                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11085                   return;
11086               }
11087               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11088               el.fxDefaults = {stopFx: true};
11089               // maximum custom styling
11090               //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
11091               el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');              
11092               tipTitle = el.child('h3');
11093               tipTitle.enableDisplayMode("block");
11094               tipBody = el.child('div.x-tip-bd');
11095               tipBodyText = el.child('div.x-tip-bd-inner');
11096               //bdLeft = el.child('div.x-tip-bd-left');
11097               //bdRight = el.child('div.x-tip-bd-right');
11098               close = el.child('div.x-tip-close');
11099               close.enableDisplayMode("block");
11100               close.on("click", hide);
11101               var d = Roo.get(document);
11102               d.on("mousedown", onDown);
11103               d.on("mouseover", onOver);
11104               d.on("mouseout", onOut);
11105               d.on("mousemove", onMove);
11106               esc = d.addKeyListener(27, hide);
11107               esc.disable();
11108               if(Roo.dd.DD){
11109                   dd = el.initDD("default", null, {
11110                       onDrag : function(){
11111                           el.sync();  
11112                       }
11113                   });
11114                   dd.setHandleElId(tipTitle.id);
11115                   dd.lock();
11116               }
11117               inited = true;
11118           }
11119           this.enable(); 
11120        },
11121
11122     /**
11123      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11124      * are supported:
11125      * <pre>
11126 Property    Type                   Description
11127 ----------  ---------------------  ------------------------------------------------------------------------
11128 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11129      * </ul>
11130      * @param {Object} config The config object
11131      */
11132        register : function(config){
11133            var cs = config instanceof Array ? config : arguments;
11134            for(var i = 0, len = cs.length; i < len; i++) {
11135                var c = cs[i];
11136                var target = c.target;
11137                if(target){
11138                    if(target instanceof Array){
11139                        for(var j = 0, jlen = target.length; j < jlen; j++){
11140                            tagEls[target[j]] = c;
11141                        }
11142                    }else{
11143                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11144                    }
11145                }
11146            }
11147        },
11148
11149     /**
11150      * Removes this quick tip from its element and destroys it.
11151      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11152      */
11153        unregister : function(el){
11154            delete tagEls[Roo.id(el)];
11155        },
11156
11157     /**
11158      * Enable this quick tip.
11159      */
11160        enable : function(){
11161            if(inited && disabled){
11162                locks.pop();
11163                if(locks.length < 1){
11164                    disabled = false;
11165                }
11166            }
11167        },
11168
11169     /**
11170      * Disable this quick tip.
11171      */
11172        disable : function(){
11173           disabled = true;
11174           clearTimeout(showProc);
11175           clearTimeout(hideProc);
11176           clearTimeout(dismissProc);
11177           if(ce){
11178               hide(true);
11179           }
11180           locks.push(1);
11181        },
11182
11183     /**
11184      * Returns true if the quick tip is enabled, else false.
11185      */
11186        isEnabled : function(){
11187             return !disabled;
11188        },
11189
11190         // private
11191        tagConfig : {
11192            namespace : "roo", // was ext?? this may break..
11193            alt_namespace : "ext",
11194            attribute : "qtip",
11195            width : "width",
11196            target : "target",
11197            title : "qtitle",
11198            hide : "hide",
11199            cls : "qclass"
11200        }
11201    };
11202 }();
11203
11204 // backwards compat
11205 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11206  * Based on:
11207  * Ext JS Library 1.1.1
11208  * Copyright(c) 2006-2007, Ext JS, LLC.
11209  *
11210  * Originally Released Under LGPL - original licence link has changed is not relivant.
11211  *
11212  * Fork - LGPL
11213  * <script type="text/javascript">
11214  */
11215  
11216
11217 /**
11218  * @class Roo.tree.TreePanel
11219  * @extends Roo.data.Tree
11220  * @cfg {Roo.tree.TreeNode} root The root node
11221  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11222  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11223  * @cfg {Boolean} enableDD true to enable drag and drop
11224  * @cfg {Boolean} enableDrag true to enable just drag
11225  * @cfg {Boolean} enableDrop true to enable just drop
11226  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11227  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11228  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11229  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11230  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11231  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11232  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11233  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11234  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11235  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11236  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11237  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
11238  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
11239  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11240  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
11241  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
11242  * 
11243  * @constructor
11244  * @param {String/HTMLElement/Element} el The container element
11245  * @param {Object} config
11246  */
11247 Roo.tree.TreePanel = function(el, config){
11248     var root = false;
11249     var loader = false;
11250     if (config.root) {
11251         root = config.root;
11252         delete config.root;
11253     }
11254     if (config.loader) {
11255         loader = config.loader;
11256         delete config.loader;
11257     }
11258     
11259     Roo.apply(this, config);
11260     Roo.tree.TreePanel.superclass.constructor.call(this);
11261     this.el = Roo.get(el);
11262     this.el.addClass('x-tree');
11263     //console.log(root);
11264     if (root) {
11265         this.setRootNode( Roo.factory(root, Roo.tree));
11266     }
11267     if (loader) {
11268         this.loader = Roo.factory(loader, Roo.tree);
11269     }
11270    /**
11271     * Read-only. The id of the container element becomes this TreePanel's id.
11272     */
11273     this.id = this.el.id;
11274     this.addEvents({
11275         /**
11276         * @event beforeload
11277         * Fires before a node is loaded, return false to cancel
11278         * @param {Node} node The node being loaded
11279         */
11280         "beforeload" : true,
11281         /**
11282         * @event load
11283         * Fires when a node is loaded
11284         * @param {Node} node The node that was loaded
11285         */
11286         "load" : true,
11287         /**
11288         * @event textchange
11289         * Fires when the text for a node is changed
11290         * @param {Node} node The node
11291         * @param {String} text The new text
11292         * @param {String} oldText The old text
11293         */
11294         "textchange" : true,
11295         /**
11296         * @event beforeexpand
11297         * Fires before a node is expanded, return false to cancel.
11298         * @param {Node} node The node
11299         * @param {Boolean} deep
11300         * @param {Boolean} anim
11301         */
11302         "beforeexpand" : true,
11303         /**
11304         * @event beforecollapse
11305         * Fires before a node is collapsed, return false to cancel.
11306         * @param {Node} node The node
11307         * @param {Boolean} deep
11308         * @param {Boolean} anim
11309         */
11310         "beforecollapse" : true,
11311         /**
11312         * @event expand
11313         * Fires when a node is expanded
11314         * @param {Node} node The node
11315         */
11316         "expand" : true,
11317         /**
11318         * @event disabledchange
11319         * Fires when the disabled status of a node changes
11320         * @param {Node} node The node
11321         * @param {Boolean} disabled
11322         */
11323         "disabledchange" : true,
11324         /**
11325         * @event collapse
11326         * Fires when a node is collapsed
11327         * @param {Node} node The node
11328         */
11329         "collapse" : true,
11330         /**
11331         * @event beforeclick
11332         * Fires before click processing on a node. Return false to cancel the default action.
11333         * @param {Node} node The node
11334         * @param {Roo.EventObject} e The event object
11335         */
11336         "beforeclick":true,
11337         /**
11338         * @event checkchange
11339         * Fires when a node with a checkbox's checked property changes
11340         * @param {Node} this This node
11341         * @param {Boolean} checked
11342         */
11343         "checkchange":true,
11344         /**
11345         * @event click
11346         * Fires when a node is clicked
11347         * @param {Node} node The node
11348         * @param {Roo.EventObject} e The event object
11349         */
11350         "click":true,
11351         /**
11352         * @event dblclick
11353         * Fires when a node is double clicked
11354         * @param {Node} node The node
11355         * @param {Roo.EventObject} e The event object
11356         */
11357         "dblclick":true,
11358         /**
11359         * @event contextmenu
11360         * Fires when a node is right clicked
11361         * @param {Node} node The node
11362         * @param {Roo.EventObject} e The event object
11363         */
11364         "contextmenu":true,
11365         /**
11366         * @event beforechildrenrendered
11367         * Fires right before the child nodes for a node are rendered
11368         * @param {Node} node The node
11369         */
11370         "beforechildrenrendered":true,
11371         /**
11372         * @event startdrag
11373         * Fires when a node starts being dragged
11374         * @param {Roo.tree.TreePanel} this
11375         * @param {Roo.tree.TreeNode} node
11376         * @param {event} e The raw browser event
11377         */ 
11378        "startdrag" : true,
11379        /**
11380         * @event enddrag
11381         * Fires when a drag operation is complete
11382         * @param {Roo.tree.TreePanel} this
11383         * @param {Roo.tree.TreeNode} node
11384         * @param {event} e The raw browser event
11385         */
11386        "enddrag" : true,
11387        /**
11388         * @event dragdrop
11389         * Fires when a dragged node is dropped on a valid DD target
11390         * @param {Roo.tree.TreePanel} this
11391         * @param {Roo.tree.TreeNode} node
11392         * @param {DD} dd The dd it was dropped on
11393         * @param {event} e The raw browser event
11394         */
11395        "dragdrop" : true,
11396        /**
11397         * @event beforenodedrop
11398         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11399         * passed to handlers has the following properties:<br />
11400         * <ul style="padding:5px;padding-left:16px;">
11401         * <li>tree - The TreePanel</li>
11402         * <li>target - The node being targeted for the drop</li>
11403         * <li>data - The drag data from the drag source</li>
11404         * <li>point - The point of the drop - append, above or below</li>
11405         * <li>source - The drag source</li>
11406         * <li>rawEvent - Raw mouse event</li>
11407         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11408         * to be inserted by setting them on this object.</li>
11409         * <li>cancel - Set this to true to cancel the drop.</li>
11410         * </ul>
11411         * @param {Object} dropEvent
11412         */
11413        "beforenodedrop" : true,
11414        /**
11415         * @event nodedrop
11416         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11417         * passed to handlers has the following properties:<br />
11418         * <ul style="padding:5px;padding-left:16px;">
11419         * <li>tree - The TreePanel</li>
11420         * <li>target - The node being targeted for the drop</li>
11421         * <li>data - The drag data from the drag source</li>
11422         * <li>point - The point of the drop - append, above or below</li>
11423         * <li>source - The drag source</li>
11424         * <li>rawEvent - Raw mouse event</li>
11425         * <li>dropNode - Dropped node(s).</li>
11426         * </ul>
11427         * @param {Object} dropEvent
11428         */
11429        "nodedrop" : true,
11430         /**
11431         * @event nodedragover
11432         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11433         * passed to handlers has the following properties:<br />
11434         * <ul style="padding:5px;padding-left:16px;">
11435         * <li>tree - The TreePanel</li>
11436         * <li>target - The node being targeted for the drop</li>
11437         * <li>data - The drag data from the drag source</li>
11438         * <li>point - The point of the drop - append, above or below</li>
11439         * <li>source - The drag source</li>
11440         * <li>rawEvent - Raw mouse event</li>
11441         * <li>dropNode - Drop node(s) provided by the source.</li>
11442         * <li>cancel - Set this to true to signal drop not allowed.</li>
11443         * </ul>
11444         * @param {Object} dragOverEvent
11445         */
11446        "nodedragover" : true,
11447        /**
11448         * @event appendnode
11449         * Fires when append node to the tree
11450         * @param {Roo.tree.TreePanel} this
11451         * @param {Roo.tree.TreeNode} node
11452         * @param {Number} index The index of the newly appended node
11453         */
11454        "appendnode" : true
11455         
11456     });
11457     if(this.singleExpand){
11458        this.on("beforeexpand", this.restrictExpand, this);
11459     }
11460     if (this.editor) {
11461         this.editor.tree = this;
11462         this.editor = Roo.factory(this.editor, Roo.tree);
11463     }
11464     
11465     if (this.selModel) {
11466         this.selModel = Roo.factory(this.selModel, Roo.tree);
11467     }
11468    
11469 };
11470 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11471     rootVisible : true,
11472     animate: Roo.enableFx,
11473     lines : true,
11474     enableDD : false,
11475     hlDrop : Roo.enableFx,
11476   
11477     renderer: false,
11478     
11479     rendererTip: false,
11480     // private
11481     restrictExpand : function(node){
11482         var p = node.parentNode;
11483         if(p){
11484             if(p.expandedChild && p.expandedChild.parentNode == p){
11485                 p.expandedChild.collapse();
11486             }
11487             p.expandedChild = node;
11488         }
11489     },
11490
11491     // private override
11492     setRootNode : function(node){
11493         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11494         if(!this.rootVisible){
11495             node.ui = new Roo.tree.RootTreeNodeUI(node);
11496         }
11497         return node;
11498     },
11499
11500     /**
11501      * Returns the container element for this TreePanel
11502      */
11503     getEl : function(){
11504         return this.el;
11505     },
11506
11507     /**
11508      * Returns the default TreeLoader for this TreePanel
11509      */
11510     getLoader : function(){
11511         return this.loader;
11512     },
11513
11514     /**
11515      * Expand all nodes
11516      */
11517     expandAll : function(){
11518         this.root.expand(true);
11519     },
11520
11521     /**
11522      * Collapse all nodes
11523      */
11524     collapseAll : function(){
11525         this.root.collapse(true);
11526     },
11527
11528     /**
11529      * Returns the selection model used by this TreePanel
11530      */
11531     getSelectionModel : function(){
11532         if(!this.selModel){
11533             this.selModel = new Roo.tree.DefaultSelectionModel();
11534         }
11535         return this.selModel;
11536     },
11537
11538     /**
11539      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11540      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11541      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11542      * @return {Array}
11543      */
11544     getChecked : function(a, startNode){
11545         startNode = startNode || this.root;
11546         var r = [];
11547         var f = function(){
11548             if(this.attributes.checked){
11549                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11550             }
11551         }
11552         startNode.cascade(f);
11553         return r;
11554     },
11555
11556     /**
11557      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11558      * @param {String} path
11559      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11560      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11561      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11562      */
11563     expandPath : function(path, attr, callback){
11564         attr = attr || "id";
11565         var keys = path.split(this.pathSeparator);
11566         var curNode = this.root;
11567         if(curNode.attributes[attr] != keys[1]){ // invalid root
11568             if(callback){
11569                 callback(false, null);
11570             }
11571             return;
11572         }
11573         var index = 1;
11574         var f = function(){
11575             if(++index == keys.length){
11576                 if(callback){
11577                     callback(true, curNode);
11578                 }
11579                 return;
11580             }
11581             var c = curNode.findChild(attr, keys[index]);
11582             if(!c){
11583                 if(callback){
11584                     callback(false, curNode);
11585                 }
11586                 return;
11587             }
11588             curNode = c;
11589             c.expand(false, false, f);
11590         };
11591         curNode.expand(false, false, f);
11592     },
11593
11594     /**
11595      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11596      * @param {String} path
11597      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11598      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11599      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11600      */
11601     selectPath : function(path, attr, callback){
11602         attr = attr || "id";
11603         var keys = path.split(this.pathSeparator);
11604         var v = keys.pop();
11605         if(keys.length > 0){
11606             var f = function(success, node){
11607                 if(success && node){
11608                     var n = node.findChild(attr, v);
11609                     if(n){
11610                         n.select();
11611                         if(callback){
11612                             callback(true, n);
11613                         }
11614                     }else if(callback){
11615                         callback(false, n);
11616                     }
11617                 }else{
11618                     if(callback){
11619                         callback(false, n);
11620                     }
11621                 }
11622             };
11623             this.expandPath(keys.join(this.pathSeparator), attr, f);
11624         }else{
11625             this.root.select();
11626             if(callback){
11627                 callback(true, this.root);
11628             }
11629         }
11630     },
11631
11632     getTreeEl : function(){
11633         return this.el;
11634     },
11635
11636     /**
11637      * Trigger rendering of this TreePanel
11638      */
11639     render : function(){
11640         if (this.innerCt) {
11641             return this; // stop it rendering more than once!!
11642         }
11643         
11644         this.innerCt = this.el.createChild({tag:"ul",
11645                cls:"x-tree-root-ct " +
11646                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
11647
11648         if(this.containerScroll){
11649             Roo.dd.ScrollManager.register(this.el);
11650         }
11651         if((this.enableDD || this.enableDrop) && !this.dropZone){
11652            /**
11653             * The dropZone used by this tree if drop is enabled
11654             * @type Roo.tree.TreeDropZone
11655             */
11656              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
11657                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
11658            });
11659         }
11660         if((this.enableDD || this.enableDrag) && !this.dragZone){
11661            /**
11662             * The dragZone used by this tree if drag is enabled
11663             * @type Roo.tree.TreeDragZone
11664             */
11665             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
11666                ddGroup: this.ddGroup || "TreeDD",
11667                scroll: this.ddScroll
11668            });
11669         }
11670         this.getSelectionModel().init(this);
11671         if (!this.root) {
11672             Roo.log("ROOT not set in tree");
11673             return this;
11674         }
11675         this.root.render();
11676         if(!this.rootVisible){
11677             this.root.renderChildren();
11678         }
11679         return this;
11680     }
11681 });/*
11682  * Based on:
11683  * Ext JS Library 1.1.1
11684  * Copyright(c) 2006-2007, Ext JS, LLC.
11685  *
11686  * Originally Released Under LGPL - original licence link has changed is not relivant.
11687  *
11688  * Fork - LGPL
11689  * <script type="text/javascript">
11690  */
11691  
11692
11693 /**
11694  * @class Roo.tree.DefaultSelectionModel
11695  * @extends Roo.util.Observable
11696  * The default single selection for a TreePanel.
11697  * @param {Object} cfg Configuration
11698  */
11699 Roo.tree.DefaultSelectionModel = function(cfg){
11700    this.selNode = null;
11701    
11702    
11703    
11704    this.addEvents({
11705        /**
11706         * @event selectionchange
11707         * Fires when the selected node changes
11708         * @param {DefaultSelectionModel} this
11709         * @param {TreeNode} node the new selection
11710         */
11711        "selectionchange" : true,
11712
11713        /**
11714         * @event beforeselect
11715         * Fires before the selected node changes, return false to cancel the change
11716         * @param {DefaultSelectionModel} this
11717         * @param {TreeNode} node the new selection
11718         * @param {TreeNode} node the old selection
11719         */
11720        "beforeselect" : true
11721    });
11722    
11723     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
11724 };
11725
11726 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
11727     init : function(tree){
11728         this.tree = tree;
11729         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11730         tree.on("click", this.onNodeClick, this);
11731     },
11732     
11733     onNodeClick : function(node, e){
11734         if (e.ctrlKey && this.selNode == node)  {
11735             this.unselect(node);
11736             return;
11737         }
11738         this.select(node);
11739     },
11740     
11741     /**
11742      * Select a node.
11743      * @param {TreeNode} node The node to select
11744      * @return {TreeNode} The selected node
11745      */
11746     select : function(node){
11747         var last = this.selNode;
11748         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
11749             if(last){
11750                 last.ui.onSelectedChange(false);
11751             }
11752             this.selNode = node;
11753             node.ui.onSelectedChange(true);
11754             this.fireEvent("selectionchange", this, node, last);
11755         }
11756         return node;
11757     },
11758     
11759     /**
11760      * Deselect a node.
11761      * @param {TreeNode} node The node to unselect
11762      */
11763     unselect : function(node){
11764         if(this.selNode == node){
11765             this.clearSelections();
11766         }    
11767     },
11768     
11769     /**
11770      * Clear all selections
11771      */
11772     clearSelections : function(){
11773         var n = this.selNode;
11774         if(n){
11775             n.ui.onSelectedChange(false);
11776             this.selNode = null;
11777             this.fireEvent("selectionchange", this, null);
11778         }
11779         return n;
11780     },
11781     
11782     /**
11783      * Get the selected node
11784      * @return {TreeNode} The selected node
11785      */
11786     getSelectedNode : function(){
11787         return this.selNode;    
11788     },
11789     
11790     /**
11791      * Returns true if the node is selected
11792      * @param {TreeNode} node The node to check
11793      * @return {Boolean}
11794      */
11795     isSelected : function(node){
11796         return this.selNode == node;  
11797     },
11798
11799     /**
11800      * Selects the node above the selected node in the tree, intelligently walking the nodes
11801      * @return TreeNode The new selection
11802      */
11803     selectPrevious : function(){
11804         var s = this.selNode || this.lastSelNode;
11805         if(!s){
11806             return null;
11807         }
11808         var ps = s.previousSibling;
11809         if(ps){
11810             if(!ps.isExpanded() || ps.childNodes.length < 1){
11811                 return this.select(ps);
11812             } else{
11813                 var lc = ps.lastChild;
11814                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
11815                     lc = lc.lastChild;
11816                 }
11817                 return this.select(lc);
11818             }
11819         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
11820             return this.select(s.parentNode);
11821         }
11822         return null;
11823     },
11824
11825     /**
11826      * Selects the node above the selected node in the tree, intelligently walking the nodes
11827      * @return TreeNode The new selection
11828      */
11829     selectNext : function(){
11830         var s = this.selNode || this.lastSelNode;
11831         if(!s){
11832             return null;
11833         }
11834         if(s.firstChild && s.isExpanded()){
11835              return this.select(s.firstChild);
11836          }else if(s.nextSibling){
11837              return this.select(s.nextSibling);
11838          }else if(s.parentNode){
11839             var newS = null;
11840             s.parentNode.bubble(function(){
11841                 if(this.nextSibling){
11842                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
11843                     return false;
11844                 }
11845             });
11846             return newS;
11847          }
11848         return null;
11849     },
11850
11851     onKeyDown : function(e){
11852         var s = this.selNode || this.lastSelNode;
11853         // undesirable, but required
11854         var sm = this;
11855         if(!s){
11856             return;
11857         }
11858         var k = e.getKey();
11859         switch(k){
11860              case e.DOWN:
11861                  e.stopEvent();
11862                  this.selectNext();
11863              break;
11864              case e.UP:
11865                  e.stopEvent();
11866                  this.selectPrevious();
11867              break;
11868              case e.RIGHT:
11869                  e.preventDefault();
11870                  if(s.hasChildNodes()){
11871                      if(!s.isExpanded()){
11872                          s.expand();
11873                      }else if(s.firstChild){
11874                          this.select(s.firstChild, e);
11875                      }
11876                  }
11877              break;
11878              case e.LEFT:
11879                  e.preventDefault();
11880                  if(s.hasChildNodes() && s.isExpanded()){
11881                      s.collapse();
11882                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
11883                      this.select(s.parentNode, e);
11884                  }
11885              break;
11886         };
11887     }
11888 });
11889
11890 /**
11891  * @class Roo.tree.MultiSelectionModel
11892  * @extends Roo.util.Observable
11893  * Multi selection for a TreePanel.
11894  * @param {Object} cfg Configuration
11895  */
11896 Roo.tree.MultiSelectionModel = function(){
11897    this.selNodes = [];
11898    this.selMap = {};
11899    this.addEvents({
11900        /**
11901         * @event selectionchange
11902         * Fires when the selected nodes change
11903         * @param {MultiSelectionModel} this
11904         * @param {Array} nodes Array of the selected nodes
11905         */
11906        "selectionchange" : true
11907    });
11908    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
11909    
11910 };
11911
11912 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
11913     init : function(tree){
11914         this.tree = tree;
11915         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11916         tree.on("click", this.onNodeClick, this);
11917     },
11918     
11919     onNodeClick : function(node, e){
11920         this.select(node, e, e.ctrlKey);
11921     },
11922     
11923     /**
11924      * Select a node.
11925      * @param {TreeNode} node The node to select
11926      * @param {EventObject} e (optional) An event associated with the selection
11927      * @param {Boolean} keepExisting True to retain existing selections
11928      * @return {TreeNode} The selected node
11929      */
11930     select : function(node, e, keepExisting){
11931         if(keepExisting !== true){
11932             this.clearSelections(true);
11933         }
11934         if(this.isSelected(node)){
11935             this.lastSelNode = node;
11936             return node;
11937         }
11938         this.selNodes.push(node);
11939         this.selMap[node.id] = node;
11940         this.lastSelNode = node;
11941         node.ui.onSelectedChange(true);
11942         this.fireEvent("selectionchange", this, this.selNodes);
11943         return node;
11944     },
11945     
11946     /**
11947      * Deselect a node.
11948      * @param {TreeNode} node The node to unselect
11949      */
11950     unselect : function(node){
11951         if(this.selMap[node.id]){
11952             node.ui.onSelectedChange(false);
11953             var sn = this.selNodes;
11954             var index = -1;
11955             if(sn.indexOf){
11956                 index = sn.indexOf(node);
11957             }else{
11958                 for(var i = 0, len = sn.length; i < len; i++){
11959                     if(sn[i] == node){
11960                         index = i;
11961                         break;
11962                     }
11963                 }
11964             }
11965             if(index != -1){
11966                 this.selNodes.splice(index, 1);
11967             }
11968             delete this.selMap[node.id];
11969             this.fireEvent("selectionchange", this, this.selNodes);
11970         }
11971     },
11972     
11973     /**
11974      * Clear all selections
11975      */
11976     clearSelections : function(suppressEvent){
11977         var sn = this.selNodes;
11978         if(sn.length > 0){
11979             for(var i = 0, len = sn.length; i < len; i++){
11980                 sn[i].ui.onSelectedChange(false);
11981             }
11982             this.selNodes = [];
11983             this.selMap = {};
11984             if(suppressEvent !== true){
11985                 this.fireEvent("selectionchange", this, this.selNodes);
11986             }
11987         }
11988     },
11989     
11990     /**
11991      * Returns true if the node is selected
11992      * @param {TreeNode} node The node to check
11993      * @return {Boolean}
11994      */
11995     isSelected : function(node){
11996         return this.selMap[node.id] ? true : false;  
11997     },
11998     
11999     /**
12000      * Returns an array of the selected nodes
12001      * @return {Array}
12002      */
12003     getSelectedNodes : function(){
12004         return this.selNodes;    
12005     },
12006
12007     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12008
12009     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12010
12011     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12012 });/*
12013  * Based on:
12014  * Ext JS Library 1.1.1
12015  * Copyright(c) 2006-2007, Ext JS, LLC.
12016  *
12017  * Originally Released Under LGPL - original licence link has changed is not relivant.
12018  *
12019  * Fork - LGPL
12020  * <script type="text/javascript">
12021  */
12022  
12023 /**
12024  * @class Roo.tree.TreeNode
12025  * @extends Roo.data.Node
12026  * @cfg {String} text The text for this node
12027  * @cfg {Boolean} expanded true to start the node expanded
12028  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12029  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12030  * @cfg {Boolean} disabled true to start the node disabled
12031  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12032  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12033  * @cfg {String} cls A css class to be added to the node
12034  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12035  * @cfg {String} href URL of the link used for the node (defaults to #)
12036  * @cfg {String} hrefTarget target frame for the link
12037  * @cfg {String} qtip An Ext QuickTip for the node
12038  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12039  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12040  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12041  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12042  * (defaults to undefined with no checkbox rendered)
12043  * @constructor
12044  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12045  */
12046 Roo.tree.TreeNode = function(attributes){
12047     attributes = attributes || {};
12048     if(typeof attributes == "string"){
12049         attributes = {text: attributes};
12050     }
12051     this.childrenRendered = false;
12052     this.rendered = false;
12053     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12054     this.expanded = attributes.expanded === true;
12055     this.isTarget = attributes.isTarget !== false;
12056     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12057     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12058
12059     /**
12060      * Read-only. The text for this node. To change it use setText().
12061      * @type String
12062      */
12063     this.text = attributes.text;
12064     /**
12065      * True if this node is disabled.
12066      * @type Boolean
12067      */
12068     this.disabled = attributes.disabled === true;
12069
12070     this.addEvents({
12071         /**
12072         * @event textchange
12073         * Fires when the text for this node is changed
12074         * @param {Node} this This node
12075         * @param {String} text The new text
12076         * @param {String} oldText The old text
12077         */
12078         "textchange" : true,
12079         /**
12080         * @event beforeexpand
12081         * Fires before this node is expanded, return false to cancel.
12082         * @param {Node} this This node
12083         * @param {Boolean} deep
12084         * @param {Boolean} anim
12085         */
12086         "beforeexpand" : true,
12087         /**
12088         * @event beforecollapse
12089         * Fires before this node is collapsed, return false to cancel.
12090         * @param {Node} this This node
12091         * @param {Boolean} deep
12092         * @param {Boolean} anim
12093         */
12094         "beforecollapse" : true,
12095         /**
12096         * @event expand
12097         * Fires when this node is expanded
12098         * @param {Node} this This node
12099         */
12100         "expand" : true,
12101         /**
12102         * @event disabledchange
12103         * Fires when the disabled status of this node changes
12104         * @param {Node} this This node
12105         * @param {Boolean} disabled
12106         */
12107         "disabledchange" : true,
12108         /**
12109         * @event collapse
12110         * Fires when this node is collapsed
12111         * @param {Node} this This node
12112         */
12113         "collapse" : true,
12114         /**
12115         * @event beforeclick
12116         * Fires before click processing. Return false to cancel the default action.
12117         * @param {Node} this This node
12118         * @param {Roo.EventObject} e The event object
12119         */
12120         "beforeclick":true,
12121         /**
12122         * @event checkchange
12123         * Fires when a node with a checkbox's checked property changes
12124         * @param {Node} this This node
12125         * @param {Boolean} checked
12126         */
12127         "checkchange":true,
12128         /**
12129         * @event click
12130         * Fires when this node is clicked
12131         * @param {Node} this This node
12132         * @param {Roo.EventObject} e The event object
12133         */
12134         "click":true,
12135         /**
12136         * @event dblclick
12137         * Fires when this node is double clicked
12138         * @param {Node} this This node
12139         * @param {Roo.EventObject} e The event object
12140         */
12141         "dblclick":true,
12142         /**
12143         * @event contextmenu
12144         * Fires when this node is right clicked
12145         * @param {Node} this This node
12146         * @param {Roo.EventObject} e The event object
12147         */
12148         "contextmenu":true,
12149         /**
12150         * @event beforechildrenrendered
12151         * Fires right before the child nodes for this node are rendered
12152         * @param {Node} this This node
12153         */
12154         "beforechildrenrendered":true
12155     });
12156
12157     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12158
12159     /**
12160      * Read-only. The UI for this node
12161      * @type TreeNodeUI
12162      */
12163     this.ui = new uiClass(this);
12164     
12165     // finally support items[]
12166     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12167         return;
12168     }
12169     
12170     
12171     Roo.each(this.attributes.items, function(c) {
12172         this.appendChild(Roo.factory(c,Roo.Tree));
12173     }, this);
12174     delete this.attributes.items;
12175     
12176     
12177     
12178 };
12179 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12180     preventHScroll: true,
12181     /**
12182      * Returns true if this node is expanded
12183      * @return {Boolean}
12184      */
12185     isExpanded : function(){
12186         return this.expanded;
12187     },
12188
12189     /**
12190      * Returns the UI object for this node
12191      * @return {TreeNodeUI}
12192      */
12193     getUI : function(){
12194         return this.ui;
12195     },
12196
12197     // private override
12198     setFirstChild : function(node){
12199         var of = this.firstChild;
12200         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12201         if(this.childrenRendered && of && node != of){
12202             of.renderIndent(true, true);
12203         }
12204         if(this.rendered){
12205             this.renderIndent(true, true);
12206         }
12207     },
12208
12209     // private override
12210     setLastChild : function(node){
12211         var ol = this.lastChild;
12212         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12213         if(this.childrenRendered && ol && node != ol){
12214             ol.renderIndent(true, true);
12215         }
12216         if(this.rendered){
12217             this.renderIndent(true, true);
12218         }
12219     },
12220
12221     // these methods are overridden to provide lazy rendering support
12222     // private override
12223     appendChild : function()
12224     {
12225         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12226         if(node && this.childrenRendered){
12227             node.render();
12228         }
12229         this.ui.updateExpandIcon();
12230         return node;
12231     },
12232
12233     // private override
12234     removeChild : function(node){
12235         this.ownerTree.getSelectionModel().unselect(node);
12236         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12237         // if it's been rendered remove dom node
12238         if(this.childrenRendered){
12239             node.ui.remove();
12240         }
12241         if(this.childNodes.length < 1){
12242             this.collapse(false, false);
12243         }else{
12244             this.ui.updateExpandIcon();
12245         }
12246         if(!this.firstChild) {
12247             this.childrenRendered = false;
12248         }
12249         return node;
12250     },
12251
12252     // private override
12253     insertBefore : function(node, refNode){
12254         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12255         if(newNode && refNode && this.childrenRendered){
12256             node.render();
12257         }
12258         this.ui.updateExpandIcon();
12259         return newNode;
12260     },
12261
12262     /**
12263      * Sets the text for this node
12264      * @param {String} text
12265      */
12266     setText : function(text){
12267         var oldText = this.text;
12268         this.text = text;
12269         this.attributes.text = text;
12270         if(this.rendered){ // event without subscribing
12271             this.ui.onTextChange(this, text, oldText);
12272         }
12273         this.fireEvent("textchange", this, text, oldText);
12274     },
12275
12276     /**
12277      * Triggers selection of this node
12278      */
12279     select : function(){
12280         this.getOwnerTree().getSelectionModel().select(this);
12281     },
12282
12283     /**
12284      * Triggers deselection of this node
12285      */
12286     unselect : function(){
12287         this.getOwnerTree().getSelectionModel().unselect(this);
12288     },
12289
12290     /**
12291      * Returns true if this node is selected
12292      * @return {Boolean}
12293      */
12294     isSelected : function(){
12295         return this.getOwnerTree().getSelectionModel().isSelected(this);
12296     },
12297
12298     /**
12299      * Expand this node.
12300      * @param {Boolean} deep (optional) True to expand all children as well
12301      * @param {Boolean} anim (optional) false to cancel the default animation
12302      * @param {Function} callback (optional) A callback to be called when
12303      * expanding this node completes (does not wait for deep expand to complete).
12304      * Called with 1 parameter, this node.
12305      */
12306     expand : function(deep, anim, callback){
12307         if(!this.expanded){
12308             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12309                 return;
12310             }
12311             if(!this.childrenRendered){
12312                 this.renderChildren();
12313             }
12314             this.expanded = true;
12315             
12316             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12317                 this.ui.animExpand(function(){
12318                     this.fireEvent("expand", this);
12319                     if(typeof callback == "function"){
12320                         callback(this);
12321                     }
12322                     if(deep === true){
12323                         this.expandChildNodes(true);
12324                     }
12325                 }.createDelegate(this));
12326                 return;
12327             }else{
12328                 this.ui.expand();
12329                 this.fireEvent("expand", this);
12330                 if(typeof callback == "function"){
12331                     callback(this);
12332                 }
12333             }
12334         }else{
12335            if(typeof callback == "function"){
12336                callback(this);
12337            }
12338         }
12339         if(deep === true){
12340             this.expandChildNodes(true);
12341         }
12342     },
12343
12344     isHiddenRoot : function(){
12345         return this.isRoot && !this.getOwnerTree().rootVisible;
12346     },
12347
12348     /**
12349      * Collapse this node.
12350      * @param {Boolean} deep (optional) True to collapse all children as well
12351      * @param {Boolean} anim (optional) false to cancel the default animation
12352      */
12353     collapse : function(deep, anim){
12354         if(this.expanded && !this.isHiddenRoot()){
12355             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12356                 return;
12357             }
12358             this.expanded = false;
12359             if((this.getOwnerTree().animate && anim !== false) || anim){
12360                 this.ui.animCollapse(function(){
12361                     this.fireEvent("collapse", this);
12362                     if(deep === true){
12363                         this.collapseChildNodes(true);
12364                     }
12365                 }.createDelegate(this));
12366                 return;
12367             }else{
12368                 this.ui.collapse();
12369                 this.fireEvent("collapse", this);
12370             }
12371         }
12372         if(deep === true){
12373             var cs = this.childNodes;
12374             for(var i = 0, len = cs.length; i < len; i++) {
12375                 cs[i].collapse(true, false);
12376             }
12377         }
12378     },
12379
12380     // private
12381     delayedExpand : function(delay){
12382         if(!this.expandProcId){
12383             this.expandProcId = this.expand.defer(delay, this);
12384         }
12385     },
12386
12387     // private
12388     cancelExpand : function(){
12389         if(this.expandProcId){
12390             clearTimeout(this.expandProcId);
12391         }
12392         this.expandProcId = false;
12393     },
12394
12395     /**
12396      * Toggles expanded/collapsed state of the node
12397      */
12398     toggle : function(){
12399         if(this.expanded){
12400             this.collapse();
12401         }else{
12402             this.expand();
12403         }
12404     },
12405
12406     /**
12407      * Ensures all parent nodes are expanded
12408      */
12409     ensureVisible : function(callback){
12410         var tree = this.getOwnerTree();
12411         tree.expandPath(this.parentNode.getPath(), false, function(){
12412             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12413             Roo.callback(callback);
12414         }.createDelegate(this));
12415     },
12416
12417     /**
12418      * Expand all child nodes
12419      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12420      */
12421     expandChildNodes : function(deep){
12422         var cs = this.childNodes;
12423         for(var i = 0, len = cs.length; i < len; i++) {
12424                 cs[i].expand(deep);
12425         }
12426     },
12427
12428     /**
12429      * Collapse all child nodes
12430      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12431      */
12432     collapseChildNodes : function(deep){
12433         var cs = this.childNodes;
12434         for(var i = 0, len = cs.length; i < len; i++) {
12435                 cs[i].collapse(deep);
12436         }
12437     },
12438
12439     /**
12440      * Disables this node
12441      */
12442     disable : function(){
12443         this.disabled = true;
12444         this.unselect();
12445         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12446             this.ui.onDisableChange(this, true);
12447         }
12448         this.fireEvent("disabledchange", this, true);
12449     },
12450
12451     /**
12452      * Enables this node
12453      */
12454     enable : function(){
12455         this.disabled = false;
12456         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12457             this.ui.onDisableChange(this, false);
12458         }
12459         this.fireEvent("disabledchange", this, false);
12460     },
12461
12462     // private
12463     renderChildren : function(suppressEvent){
12464         if(suppressEvent !== false){
12465             this.fireEvent("beforechildrenrendered", this);
12466         }
12467         var cs = this.childNodes;
12468         for(var i = 0, len = cs.length; i < len; i++){
12469             cs[i].render(true);
12470         }
12471         this.childrenRendered = true;
12472     },
12473
12474     // private
12475     sort : function(fn, scope){
12476         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12477         if(this.childrenRendered){
12478             var cs = this.childNodes;
12479             for(var i = 0, len = cs.length; i < len; i++){
12480                 cs[i].render(true);
12481             }
12482         }
12483     },
12484
12485     // private
12486     render : function(bulkRender){
12487         this.ui.render(bulkRender);
12488         if(!this.rendered){
12489             this.rendered = true;
12490             if(this.expanded){
12491                 this.expanded = false;
12492                 this.expand(false, false);
12493             }
12494         }
12495     },
12496
12497     // private
12498     renderIndent : function(deep, refresh){
12499         if(refresh){
12500             this.ui.childIndent = null;
12501         }
12502         this.ui.renderIndent();
12503         if(deep === true && this.childrenRendered){
12504             var cs = this.childNodes;
12505             for(var i = 0, len = cs.length; i < len; i++){
12506                 cs[i].renderIndent(true, refresh);
12507             }
12508         }
12509     }
12510 });/*
12511  * Based on:
12512  * Ext JS Library 1.1.1
12513  * Copyright(c) 2006-2007, Ext JS, LLC.
12514  *
12515  * Originally Released Under LGPL - original licence link has changed is not relivant.
12516  *
12517  * Fork - LGPL
12518  * <script type="text/javascript">
12519  */
12520  
12521 /**
12522  * @class Roo.tree.AsyncTreeNode
12523  * @extends Roo.tree.TreeNode
12524  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12525  * @constructor
12526  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12527  */
12528  Roo.tree.AsyncTreeNode = function(config){
12529     this.loaded = false;
12530     this.loading = false;
12531     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12532     /**
12533     * @event beforeload
12534     * Fires before this node is loaded, return false to cancel
12535     * @param {Node} this This node
12536     */
12537     this.addEvents({'beforeload':true, 'load': true});
12538     /**
12539     * @event load
12540     * Fires when this node is loaded
12541     * @param {Node} this This node
12542     */
12543     /**
12544      * The loader used by this node (defaults to using the tree's defined loader)
12545      * @type TreeLoader
12546      * @property loader
12547      */
12548 };
12549 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12550     expand : function(deep, anim, callback){
12551         if(this.loading){ // if an async load is already running, waiting til it's done
12552             var timer;
12553             var f = function(){
12554                 if(!this.loading){ // done loading
12555                     clearInterval(timer);
12556                     this.expand(deep, anim, callback);
12557                 }
12558             }.createDelegate(this);
12559             timer = setInterval(f, 200);
12560             return;
12561         }
12562         if(!this.loaded){
12563             if(this.fireEvent("beforeload", this) === false){
12564                 return;
12565             }
12566             this.loading = true;
12567             this.ui.beforeLoad(this);
12568             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12569             if(loader){
12570                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12571                 return;
12572             }
12573         }
12574         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12575     },
12576     
12577     /**
12578      * Returns true if this node is currently loading
12579      * @return {Boolean}
12580      */
12581     isLoading : function(){
12582         return this.loading;  
12583     },
12584     
12585     loadComplete : function(deep, anim, callback){
12586         this.loading = false;
12587         this.loaded = true;
12588         this.ui.afterLoad(this);
12589         this.fireEvent("load", this);
12590         this.expand(deep, anim, callback);
12591     },
12592     
12593     /**
12594      * Returns true if this node has been loaded
12595      * @return {Boolean}
12596      */
12597     isLoaded : function(){
12598         return this.loaded;
12599     },
12600     
12601     hasChildNodes : function(){
12602         if(!this.isLeaf() && !this.loaded){
12603             return true;
12604         }else{
12605             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12606         }
12607     },
12608
12609     /**
12610      * Trigger a reload for this node
12611      * @param {Function} callback
12612      */
12613     reload : function(callback){
12614         this.collapse(false, false);
12615         while(this.firstChild){
12616             this.removeChild(this.firstChild);
12617         }
12618         this.childrenRendered = false;
12619         this.loaded = false;
12620         if(this.isHiddenRoot()){
12621             this.expanded = false;
12622         }
12623         this.expand(false, false, callback);
12624     }
12625 });/*
12626  * Based on:
12627  * Ext JS Library 1.1.1
12628  * Copyright(c) 2006-2007, Ext JS, LLC.
12629  *
12630  * Originally Released Under LGPL - original licence link has changed is not relivant.
12631  *
12632  * Fork - LGPL
12633  * <script type="text/javascript">
12634  */
12635  
12636 /**
12637  * @class Roo.tree.TreeNodeUI
12638  * @constructor
12639  * @param {Object} node The node to render
12640  * The TreeNode UI implementation is separate from the
12641  * tree implementation. Unless you are customizing the tree UI,
12642  * you should never have to use this directly.
12643  */
12644 Roo.tree.TreeNodeUI = function(node){
12645     this.node = node;
12646     this.rendered = false;
12647     this.animating = false;
12648     this.emptyIcon = Roo.BLANK_IMAGE_URL;
12649 };
12650
12651 Roo.tree.TreeNodeUI.prototype = {
12652     removeChild : function(node){
12653         if(this.rendered){
12654             this.ctNode.removeChild(node.ui.getEl());
12655         }
12656     },
12657
12658     beforeLoad : function(){
12659          this.addClass("x-tree-node-loading");
12660     },
12661
12662     afterLoad : function(){
12663          this.removeClass("x-tree-node-loading");
12664     },
12665
12666     onTextChange : function(node, text, oldText){
12667         if(this.rendered){
12668             this.textNode.innerHTML = text;
12669         }
12670     },
12671
12672     onDisableChange : function(node, state){
12673         this.disabled = state;
12674         if(state){
12675             this.addClass("x-tree-node-disabled");
12676         }else{
12677             this.removeClass("x-tree-node-disabled");
12678         }
12679     },
12680
12681     onSelectedChange : function(state){
12682         if(state){
12683             this.focus();
12684             this.addClass("x-tree-selected");
12685         }else{
12686             //this.blur();
12687             this.removeClass("x-tree-selected");
12688         }
12689     },
12690
12691     onMove : function(tree, node, oldParent, newParent, index, refNode){
12692         this.childIndent = null;
12693         if(this.rendered){
12694             var targetNode = newParent.ui.getContainer();
12695             if(!targetNode){//target not rendered
12696                 this.holder = document.createElement("div");
12697                 this.holder.appendChild(this.wrap);
12698                 return;
12699             }
12700             var insertBefore = refNode ? refNode.ui.getEl() : null;
12701             if(insertBefore){
12702                 targetNode.insertBefore(this.wrap, insertBefore);
12703             }else{
12704                 targetNode.appendChild(this.wrap);
12705             }
12706             this.node.renderIndent(true);
12707         }
12708     },
12709
12710     addClass : function(cls){
12711         if(this.elNode){
12712             Roo.fly(this.elNode).addClass(cls);
12713         }
12714     },
12715
12716     removeClass : function(cls){
12717         if(this.elNode){
12718             Roo.fly(this.elNode).removeClass(cls);
12719         }
12720     },
12721
12722     remove : function(){
12723         if(this.rendered){
12724             this.holder = document.createElement("div");
12725             this.holder.appendChild(this.wrap);
12726         }
12727     },
12728
12729     fireEvent : function(){
12730         return this.node.fireEvent.apply(this.node, arguments);
12731     },
12732
12733     initEvents : function(){
12734         this.node.on("move", this.onMove, this);
12735         var E = Roo.EventManager;
12736         var a = this.anchor;
12737
12738         var el = Roo.fly(a, '_treeui');
12739
12740         if(Roo.isOpera){ // opera render bug ignores the CSS
12741             el.setStyle("text-decoration", "none");
12742         }
12743
12744         el.on("click", this.onClick, this);
12745         el.on("dblclick", this.onDblClick, this);
12746
12747         if(this.checkbox){
12748             Roo.EventManager.on(this.checkbox,
12749                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
12750         }
12751
12752         el.on("contextmenu", this.onContextMenu, this);
12753
12754         var icon = Roo.fly(this.iconNode);
12755         icon.on("click", this.onClick, this);
12756         icon.on("dblclick", this.onDblClick, this);
12757         icon.on("contextmenu", this.onContextMenu, this);
12758         E.on(this.ecNode, "click", this.ecClick, this, true);
12759
12760         if(this.node.disabled){
12761             this.addClass("x-tree-node-disabled");
12762         }
12763         if(this.node.hidden){
12764             this.addClass("x-tree-node-disabled");
12765         }
12766         var ot = this.node.getOwnerTree();
12767         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
12768         if(dd && (!this.node.isRoot || ot.rootVisible)){
12769             Roo.dd.Registry.register(this.elNode, {
12770                 node: this.node,
12771                 handles: this.getDDHandles(),
12772                 isHandle: false
12773             });
12774         }
12775     },
12776
12777     getDDHandles : function(){
12778         return [this.iconNode, this.textNode];
12779     },
12780
12781     hide : function(){
12782         if(this.rendered){
12783             this.wrap.style.display = "none";
12784         }
12785     },
12786
12787     show : function(){
12788         if(this.rendered){
12789             this.wrap.style.display = "";
12790         }
12791     },
12792
12793     onContextMenu : function(e){
12794         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
12795             e.preventDefault();
12796             this.focus();
12797             this.fireEvent("contextmenu", this.node, e);
12798         }
12799     },
12800
12801     onClick : function(e){
12802         if(this.dropping){
12803             e.stopEvent();
12804             return;
12805         }
12806         if(this.fireEvent("beforeclick", this.node, e) !== false){
12807             if(!this.disabled && this.node.attributes.href){
12808                 this.fireEvent("click", this.node, e);
12809                 return;
12810             }
12811             e.preventDefault();
12812             if(this.disabled){
12813                 return;
12814             }
12815
12816             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
12817                 this.node.toggle();
12818             }
12819
12820             this.fireEvent("click", this.node, e);
12821         }else{
12822             e.stopEvent();
12823         }
12824     },
12825
12826     onDblClick : function(e){
12827         e.preventDefault();
12828         if(this.disabled){
12829             return;
12830         }
12831         if(this.checkbox){
12832             this.toggleCheck();
12833         }
12834         if(!this.animating && this.node.hasChildNodes()){
12835             this.node.toggle();
12836         }
12837         this.fireEvent("dblclick", this.node, e);
12838     },
12839
12840     onCheckChange : function(){
12841         var checked = this.checkbox.checked;
12842         this.node.attributes.checked = checked;
12843         this.fireEvent('checkchange', this.node, checked);
12844     },
12845
12846     ecClick : function(e){
12847         if(!this.animating && this.node.hasChildNodes()){
12848             this.node.toggle();
12849         }
12850     },
12851
12852     startDrop : function(){
12853         this.dropping = true;
12854     },
12855
12856     // delayed drop so the click event doesn't get fired on a drop
12857     endDrop : function(){
12858        setTimeout(function(){
12859            this.dropping = false;
12860        }.createDelegate(this), 50);
12861     },
12862
12863     expand : function(){
12864         this.updateExpandIcon();
12865         this.ctNode.style.display = "";
12866     },
12867
12868     focus : function(){
12869         if(!this.node.preventHScroll){
12870             try{this.anchor.focus();
12871             }catch(e){}
12872         }else if(!Roo.isIE){
12873             try{
12874                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
12875                 var l = noscroll.scrollLeft;
12876                 this.anchor.focus();
12877                 noscroll.scrollLeft = l;
12878             }catch(e){}
12879         }
12880     },
12881
12882     toggleCheck : function(value){
12883         var cb = this.checkbox;
12884         if(cb){
12885             cb.checked = (value === undefined ? !cb.checked : value);
12886         }
12887     },
12888
12889     blur : function(){
12890         try{
12891             this.anchor.blur();
12892         }catch(e){}
12893     },
12894
12895     animExpand : function(callback){
12896         var ct = Roo.get(this.ctNode);
12897         ct.stopFx();
12898         if(!this.node.hasChildNodes()){
12899             this.updateExpandIcon();
12900             this.ctNode.style.display = "";
12901             Roo.callback(callback);
12902             return;
12903         }
12904         this.animating = true;
12905         this.updateExpandIcon();
12906
12907         ct.slideIn('t', {
12908            callback : function(){
12909                this.animating = false;
12910                Roo.callback(callback);
12911             },
12912             scope: this,
12913             duration: this.node.ownerTree.duration || .25
12914         });
12915     },
12916
12917     highlight : function(){
12918         var tree = this.node.getOwnerTree();
12919         Roo.fly(this.wrap).highlight(
12920             tree.hlColor || "C3DAF9",
12921             {endColor: tree.hlBaseColor}
12922         );
12923     },
12924
12925     collapse : function(){
12926         this.updateExpandIcon();
12927         this.ctNode.style.display = "none";
12928     },
12929
12930     animCollapse : function(callback){
12931         var ct = Roo.get(this.ctNode);
12932         ct.enableDisplayMode('block');
12933         ct.stopFx();
12934
12935         this.animating = true;
12936         this.updateExpandIcon();
12937
12938         ct.slideOut('t', {
12939             callback : function(){
12940                this.animating = false;
12941                Roo.callback(callback);
12942             },
12943             scope: this,
12944             duration: this.node.ownerTree.duration || .25
12945         });
12946     },
12947
12948     getContainer : function(){
12949         return this.ctNode;
12950     },
12951
12952     getEl : function(){
12953         return this.wrap;
12954     },
12955
12956     appendDDGhost : function(ghostNode){
12957         ghostNode.appendChild(this.elNode.cloneNode(true));
12958     },
12959
12960     getDDRepairXY : function(){
12961         return Roo.lib.Dom.getXY(this.iconNode);
12962     },
12963
12964     onRender : function(){
12965         this.render();
12966     },
12967
12968     render : function(bulkRender){
12969         var n = this.node, a = n.attributes;
12970         var targetNode = n.parentNode ?
12971               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
12972
12973         if(!this.rendered){
12974             this.rendered = true;
12975
12976             this.renderElements(n, a, targetNode, bulkRender);
12977
12978             if(a.qtip){
12979                if(this.textNode.setAttributeNS){
12980                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
12981                    if(a.qtipTitle){
12982                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
12983                    }
12984                }else{
12985                    this.textNode.setAttribute("ext:qtip", a.qtip);
12986                    if(a.qtipTitle){
12987                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
12988                    }
12989                }
12990             }else if(a.qtipCfg){
12991                 a.qtipCfg.target = Roo.id(this.textNode);
12992                 Roo.QuickTips.register(a.qtipCfg);
12993             }
12994             this.initEvents();
12995             if(!this.node.expanded){
12996                 this.updateExpandIcon();
12997             }
12998         }else{
12999             if(bulkRender === true) {
13000                 targetNode.appendChild(this.wrap);
13001             }
13002         }
13003     },
13004
13005     renderElements : function(n, a, targetNode, bulkRender)
13006     {
13007         // add some indent caching, this helps performance when rendering a large tree
13008         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13009         var t = n.getOwnerTree();
13010         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13011         if (typeof(n.attributes.html) != 'undefined') {
13012             txt = n.attributes.html;
13013         }
13014         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
13015         var cb = typeof a.checked == 'boolean';
13016         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13017         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13018             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13019             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13020             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13021             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13022             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13023              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13024                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13025             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13026             "</li>"];
13027
13028         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13029             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13030                                 n.nextSibling.ui.getEl(), buf.join(""));
13031         }else{
13032             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13033         }
13034
13035         this.elNode = this.wrap.childNodes[0];
13036         this.ctNode = this.wrap.childNodes[1];
13037         var cs = this.elNode.childNodes;
13038         this.indentNode = cs[0];
13039         this.ecNode = cs[1];
13040         this.iconNode = cs[2];
13041         var index = 3;
13042         if(cb){
13043             this.checkbox = cs[3];
13044             index++;
13045         }
13046         this.anchor = cs[index];
13047         this.textNode = cs[index].firstChild;
13048     },
13049
13050     getAnchor : function(){
13051         return this.anchor;
13052     },
13053
13054     getTextEl : function(){
13055         return this.textNode;
13056     },
13057
13058     getIconEl : function(){
13059         return this.iconNode;
13060     },
13061
13062     isChecked : function(){
13063         return this.checkbox ? this.checkbox.checked : false;
13064     },
13065
13066     updateExpandIcon : function(){
13067         if(this.rendered){
13068             var n = this.node, c1, c2;
13069             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13070             var hasChild = n.hasChildNodes();
13071             if(hasChild){
13072                 if(n.expanded){
13073                     cls += "-minus";
13074                     c1 = "x-tree-node-collapsed";
13075                     c2 = "x-tree-node-expanded";
13076                 }else{
13077                     cls += "-plus";
13078                     c1 = "x-tree-node-expanded";
13079                     c2 = "x-tree-node-collapsed";
13080                 }
13081                 if(this.wasLeaf){
13082                     this.removeClass("x-tree-node-leaf");
13083                     this.wasLeaf = false;
13084                 }
13085                 if(this.c1 != c1 || this.c2 != c2){
13086                     Roo.fly(this.elNode).replaceClass(c1, c2);
13087                     this.c1 = c1; this.c2 = c2;
13088                 }
13089             }else{
13090                 // this changes non-leafs into leafs if they have no children.
13091                 // it's not very rational behaviour..
13092                 
13093                 if(!this.wasLeaf && this.node.leaf){
13094                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13095                     delete this.c1;
13096                     delete this.c2;
13097                     this.wasLeaf = true;
13098                 }
13099             }
13100             var ecc = "x-tree-ec-icon "+cls;
13101             if(this.ecc != ecc){
13102                 this.ecNode.className = ecc;
13103                 this.ecc = ecc;
13104             }
13105         }
13106     },
13107
13108     getChildIndent : function(){
13109         if(!this.childIndent){
13110             var buf = [];
13111             var p = this.node;
13112             while(p){
13113                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13114                     if(!p.isLast()) {
13115                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13116                     } else {
13117                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13118                     }
13119                 }
13120                 p = p.parentNode;
13121             }
13122             this.childIndent = buf.join("");
13123         }
13124         return this.childIndent;
13125     },
13126
13127     renderIndent : function(){
13128         if(this.rendered){
13129             var indent = "";
13130             var p = this.node.parentNode;
13131             if(p){
13132                 indent = p.ui.getChildIndent();
13133             }
13134             if(this.indentMarkup != indent){ // don't rerender if not required
13135                 this.indentNode.innerHTML = indent;
13136                 this.indentMarkup = indent;
13137             }
13138             this.updateExpandIcon();
13139         }
13140     }
13141 };
13142
13143 Roo.tree.RootTreeNodeUI = function(){
13144     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13145 };
13146 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13147     render : function(){
13148         if(!this.rendered){
13149             var targetNode = this.node.ownerTree.innerCt.dom;
13150             this.node.expanded = true;
13151             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13152             this.wrap = this.ctNode = targetNode.firstChild;
13153         }
13154     },
13155     collapse : function(){
13156     },
13157     expand : function(){
13158     }
13159 });/*
13160  * Based on:
13161  * Ext JS Library 1.1.1
13162  * Copyright(c) 2006-2007, Ext JS, LLC.
13163  *
13164  * Originally Released Under LGPL - original licence link has changed is not relivant.
13165  *
13166  * Fork - LGPL
13167  * <script type="text/javascript">
13168  */
13169 /**
13170  * @class Roo.tree.TreeLoader
13171  * @extends Roo.util.Observable
13172  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13173  * nodes from a specified URL. The response must be a javascript Array definition
13174  * who's elements are node definition objects. eg:
13175  * <pre><code>
13176 {  success : true,
13177    data :      [
13178    
13179     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13180     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13181     ]
13182 }
13183
13184
13185 </code></pre>
13186  * <br><br>
13187  * The old style respose with just an array is still supported, but not recommended.
13188  * <br><br>
13189  *
13190  * A server request is sent, and child nodes are loaded only when a node is expanded.
13191  * The loading node's id is passed to the server under the parameter name "node" to
13192  * enable the server to produce the correct child nodes.
13193  * <br><br>
13194  * To pass extra parameters, an event handler may be attached to the "beforeload"
13195  * event, and the parameters specified in the TreeLoader's baseParams property:
13196  * <pre><code>
13197     myTreeLoader.on("beforeload", function(treeLoader, node) {
13198         this.baseParams.category = node.attributes.category;
13199     }, this);
13200     
13201 </code></pre>
13202  *
13203  * This would pass an HTTP parameter called "category" to the server containing
13204  * the value of the Node's "category" attribute.
13205  * @constructor
13206  * Creates a new Treeloader.
13207  * @param {Object} config A config object containing config properties.
13208  */
13209 Roo.tree.TreeLoader = function(config){
13210     this.baseParams = {};
13211     this.requestMethod = "POST";
13212     Roo.apply(this, config);
13213
13214     this.addEvents({
13215     
13216         /**
13217          * @event beforeload
13218          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13219          * @param {Object} This TreeLoader object.
13220          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13221          * @param {Object} callback The callback function specified in the {@link #load} call.
13222          */
13223         beforeload : true,
13224         /**
13225          * @event load
13226          * Fires when the node has been successfuly loaded.
13227          * @param {Object} This TreeLoader object.
13228          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13229          * @param {Object} response The response object containing the data from the server.
13230          */
13231         load : true,
13232         /**
13233          * @event loadexception
13234          * Fires if the network request failed.
13235          * @param {Object} This TreeLoader object.
13236          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13237          * @param {Object} response The response object containing the data from the server.
13238          */
13239         loadexception : true,
13240         /**
13241          * @event create
13242          * Fires before a node is created, enabling you to return custom Node types 
13243          * @param {Object} This TreeLoader object.
13244          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13245          */
13246         create : true
13247     });
13248
13249     Roo.tree.TreeLoader.superclass.constructor.call(this);
13250 };
13251
13252 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13253     /**
13254     * @cfg {String} dataUrl The URL from which to request a Json string which
13255     * specifies an array of node definition object representing the child nodes
13256     * to be loaded.
13257     */
13258     /**
13259     * @cfg {String} requestMethod either GET or POST
13260     * defaults to POST (due to BC)
13261     * to be loaded.
13262     */
13263     /**
13264     * @cfg {Object} baseParams (optional) An object containing properties which
13265     * specify HTTP parameters to be passed to each request for child nodes.
13266     */
13267     /**
13268     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13269     * created by this loader. If the attributes sent by the server have an attribute in this object,
13270     * they take priority.
13271     */
13272     /**
13273     * @cfg {Object} uiProviders (optional) An object containing properties which
13274     * 
13275     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13276     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13277     * <i>uiProvider</i> attribute of a returned child node is a string rather
13278     * than a reference to a TreeNodeUI implementation, this that string value
13279     * is used as a property name in the uiProviders object. You can define the provider named
13280     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13281     */
13282     uiProviders : {},
13283
13284     /**
13285     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13286     * child nodes before loading.
13287     */
13288     clearOnLoad : true,
13289
13290     /**
13291     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13292     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13293     * Grid query { data : [ .....] }
13294     */
13295     
13296     root : false,
13297      /**
13298     * @cfg {String} queryParam (optional) 
13299     * Name of the query as it will be passed on the querystring (defaults to 'node')
13300     * eg. the request will be ?node=[id]
13301     */
13302     
13303     
13304     queryParam: false,
13305     
13306     /**
13307      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13308      * This is called automatically when a node is expanded, but may be used to reload
13309      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13310      * @param {Roo.tree.TreeNode} node
13311      * @param {Function} callback
13312      */
13313     load : function(node, callback){
13314         if(this.clearOnLoad){
13315             while(node.firstChild){
13316                 node.removeChild(node.firstChild);
13317             }
13318         }
13319         if(node.attributes.children){ // preloaded json children
13320             var cs = node.attributes.children;
13321             for(var i = 0, len = cs.length; i < len; i++){
13322                 node.appendChild(this.createNode(cs[i]));
13323             }
13324             if(typeof callback == "function"){
13325                 callback();
13326             }
13327         }else if(this.dataUrl){
13328             this.requestData(node, callback);
13329         }
13330     },
13331
13332     getParams: function(node){
13333         var buf = [], bp = this.baseParams;
13334         for(var key in bp){
13335             if(typeof bp[key] != "function"){
13336                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13337             }
13338         }
13339         var n = this.queryParam === false ? 'node' : this.queryParam;
13340         buf.push(n + "=", encodeURIComponent(node.id));
13341         return buf.join("");
13342     },
13343
13344     requestData : function(node, callback){
13345         if(this.fireEvent("beforeload", this, node, callback) !== false){
13346             this.transId = Roo.Ajax.request({
13347                 method:this.requestMethod,
13348                 url: this.dataUrl||this.url,
13349                 success: this.handleResponse,
13350                 failure: this.handleFailure,
13351                 scope: this,
13352                 argument: {callback: callback, node: node},
13353                 params: this.getParams(node)
13354             });
13355         }else{
13356             // if the load is cancelled, make sure we notify
13357             // the node that we are done
13358             if(typeof callback == "function"){
13359                 callback();
13360             }
13361         }
13362     },
13363
13364     isLoading : function(){
13365         return this.transId ? true : false;
13366     },
13367
13368     abort : function(){
13369         if(this.isLoading()){
13370             Roo.Ajax.abort(this.transId);
13371         }
13372     },
13373
13374     // private
13375     createNode : function(attr)
13376     {
13377         // apply baseAttrs, nice idea Corey!
13378         if(this.baseAttrs){
13379             Roo.applyIf(attr, this.baseAttrs);
13380         }
13381         if(this.applyLoader !== false){
13382             attr.loader = this;
13383         }
13384         // uiProvider = depreciated..
13385         
13386         if(typeof(attr.uiProvider) == 'string'){
13387            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13388                 /**  eval:var:attr */ eval(attr.uiProvider);
13389         }
13390         if(typeof(this.uiProviders['default']) != 'undefined') {
13391             attr.uiProvider = this.uiProviders['default'];
13392         }
13393         
13394         this.fireEvent('create', this, attr);
13395         
13396         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13397         return(attr.leaf ?
13398                         new Roo.tree.TreeNode(attr) :
13399                         new Roo.tree.AsyncTreeNode(attr));
13400     },
13401
13402     processResponse : function(response, node, callback)
13403     {
13404         var json = response.responseText;
13405         try {
13406             
13407             var o = Roo.decode(json);
13408             
13409             if (this.root === false && typeof(o.success) != undefined) {
13410                 this.root = 'data'; // the default behaviour for list like data..
13411                 }
13412                 
13413             if (this.root !== false &&  !o.success) {
13414                 // it's a failure condition.
13415                 var a = response.argument;
13416                 this.fireEvent("loadexception", this, a.node, response);
13417                 Roo.log("Load failed - should have a handler really");
13418                 return;
13419             }
13420             
13421             
13422             
13423             if (this.root !== false) {
13424                  o = o[this.root];
13425             }
13426             
13427             for(var i = 0, len = o.length; i < len; i++){
13428                 var n = this.createNode(o[i]);
13429                 if(n){
13430                     node.appendChild(n);
13431                 }
13432             }
13433             if(typeof callback == "function"){
13434                 callback(this, node);
13435             }
13436         }catch(e){
13437             this.handleFailure(response);
13438         }
13439     },
13440
13441     handleResponse : function(response){
13442         this.transId = false;
13443         var a = response.argument;
13444         this.processResponse(response, a.node, a.callback);
13445         this.fireEvent("load", this, a.node, response);
13446     },
13447
13448     handleFailure : function(response)
13449     {
13450         // should handle failure better..
13451         this.transId = false;
13452         var a = response.argument;
13453         this.fireEvent("loadexception", this, a.node, response);
13454         if(typeof a.callback == "function"){
13455             a.callback(this, a.node);
13456         }
13457     }
13458 });/*
13459  * Based on:
13460  * Ext JS Library 1.1.1
13461  * Copyright(c) 2006-2007, Ext JS, LLC.
13462  *
13463  * Originally Released Under LGPL - original licence link has changed is not relivant.
13464  *
13465  * Fork - LGPL
13466  * <script type="text/javascript">
13467  */
13468
13469 /**
13470 * @class Roo.tree.TreeFilter
13471 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13472 * @param {TreePanel} tree
13473 * @param {Object} config (optional)
13474  */
13475 Roo.tree.TreeFilter = function(tree, config){
13476     this.tree = tree;
13477     this.filtered = {};
13478     Roo.apply(this, config);
13479 };
13480
13481 Roo.tree.TreeFilter.prototype = {
13482     clearBlank:false,
13483     reverse:false,
13484     autoClear:false,
13485     remove:false,
13486
13487      /**
13488      * Filter the data by a specific attribute.
13489      * @param {String/RegExp} value Either string that the attribute value
13490      * should start with or a RegExp to test against the attribute
13491      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13492      * @param {TreeNode} startNode (optional) The node to start the filter at.
13493      */
13494     filter : function(value, attr, startNode){
13495         attr = attr || "text";
13496         var f;
13497         if(typeof value == "string"){
13498             var vlen = value.length;
13499             // auto clear empty filter
13500             if(vlen == 0 && this.clearBlank){
13501                 this.clear();
13502                 return;
13503             }
13504             value = value.toLowerCase();
13505             f = function(n){
13506                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13507             };
13508         }else if(value.exec){ // regex?
13509             f = function(n){
13510                 return value.test(n.attributes[attr]);
13511             };
13512         }else{
13513             throw 'Illegal filter type, must be string or regex';
13514         }
13515         this.filterBy(f, null, startNode);
13516         },
13517
13518     /**
13519      * Filter by a function. The passed function will be called with each
13520      * node in the tree (or from the startNode). If the function returns true, the node is kept
13521      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13522      * @param {Function} fn The filter function
13523      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13524      */
13525     filterBy : function(fn, scope, startNode){
13526         startNode = startNode || this.tree.root;
13527         if(this.autoClear){
13528             this.clear();
13529         }
13530         var af = this.filtered, rv = this.reverse;
13531         var f = function(n){
13532             if(n == startNode){
13533                 return true;
13534             }
13535             if(af[n.id]){
13536                 return false;
13537             }
13538             var m = fn.call(scope || n, n);
13539             if(!m || rv){
13540                 af[n.id] = n;
13541                 n.ui.hide();
13542                 return false;
13543             }
13544             return true;
13545         };
13546         startNode.cascade(f);
13547         if(this.remove){
13548            for(var id in af){
13549                if(typeof id != "function"){
13550                    var n = af[id];
13551                    if(n && n.parentNode){
13552                        n.parentNode.removeChild(n);
13553                    }
13554                }
13555            }
13556         }
13557     },
13558
13559     /**
13560      * Clears the current filter. Note: with the "remove" option
13561      * set a filter cannot be cleared.
13562      */
13563     clear : function(){
13564         var t = this.tree;
13565         var af = this.filtered;
13566         for(var id in af){
13567             if(typeof id != "function"){
13568                 var n = af[id];
13569                 if(n){
13570                     n.ui.show();
13571                 }
13572             }
13573         }
13574         this.filtered = {};
13575     }
13576 };
13577 /*
13578  * Based on:
13579  * Ext JS Library 1.1.1
13580  * Copyright(c) 2006-2007, Ext JS, LLC.
13581  *
13582  * Originally Released Under LGPL - original licence link has changed is not relivant.
13583  *
13584  * Fork - LGPL
13585  * <script type="text/javascript">
13586  */
13587  
13588
13589 /**
13590  * @class Roo.tree.TreeSorter
13591  * Provides sorting of nodes in a TreePanel
13592  * 
13593  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13594  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13595  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13596  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13597  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13598  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13599  * @constructor
13600  * @param {TreePanel} tree
13601  * @param {Object} config
13602  */
13603 Roo.tree.TreeSorter = function(tree, config){
13604     Roo.apply(this, config);
13605     tree.on("beforechildrenrendered", this.doSort, this);
13606     tree.on("append", this.updateSort, this);
13607     tree.on("insert", this.updateSort, this);
13608     
13609     var dsc = this.dir && this.dir.toLowerCase() == "desc";
13610     var p = this.property || "text";
13611     var sortType = this.sortType;
13612     var fs = this.folderSort;
13613     var cs = this.caseSensitive === true;
13614     var leafAttr = this.leafAttr || 'leaf';
13615
13616     this.sortFn = function(n1, n2){
13617         if(fs){
13618             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13619                 return 1;
13620             }
13621             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13622                 return -1;
13623             }
13624         }
13625         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13626         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13627         if(v1 < v2){
13628                         return dsc ? +1 : -1;
13629                 }else if(v1 > v2){
13630                         return dsc ? -1 : +1;
13631         }else{
13632                 return 0;
13633         }
13634     };
13635 };
13636
13637 Roo.tree.TreeSorter.prototype = {
13638     doSort : function(node){
13639         node.sort(this.sortFn);
13640     },
13641     
13642     compareNodes : function(n1, n2){
13643         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
13644     },
13645     
13646     updateSort : function(tree, node){
13647         if(node.childrenRendered){
13648             this.doSort.defer(1, this, [node]);
13649         }
13650     }
13651 };/*
13652  * Based on:
13653  * Ext JS Library 1.1.1
13654  * Copyright(c) 2006-2007, Ext JS, LLC.
13655  *
13656  * Originally Released Under LGPL - original licence link has changed is not relivant.
13657  *
13658  * Fork - LGPL
13659  * <script type="text/javascript">
13660  */
13661
13662 if(Roo.dd.DropZone){
13663     
13664 Roo.tree.TreeDropZone = function(tree, config){
13665     this.allowParentInsert = false;
13666     this.allowContainerDrop = false;
13667     this.appendOnly = false;
13668     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
13669     this.tree = tree;
13670     this.lastInsertClass = "x-tree-no-status";
13671     this.dragOverData = {};
13672 };
13673
13674 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
13675     ddGroup : "TreeDD",
13676     scroll:  true,
13677     
13678     expandDelay : 1000,
13679     
13680     expandNode : function(node){
13681         if(node.hasChildNodes() && !node.isExpanded()){
13682             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
13683         }
13684     },
13685     
13686     queueExpand : function(node){
13687         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
13688     },
13689     
13690     cancelExpand : function(){
13691         if(this.expandProcId){
13692             clearTimeout(this.expandProcId);
13693             this.expandProcId = false;
13694         }
13695     },
13696     
13697     isValidDropPoint : function(n, pt, dd, e, data){
13698         if(!n || !data){ return false; }
13699         var targetNode = n.node;
13700         var dropNode = data.node;
13701         // default drop rules
13702         if(!(targetNode && targetNode.isTarget && pt)){
13703             return false;
13704         }
13705         if(pt == "append" && targetNode.allowChildren === false){
13706             return false;
13707         }
13708         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
13709             return false;
13710         }
13711         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
13712             return false;
13713         }
13714         // reuse the object
13715         var overEvent = this.dragOverData;
13716         overEvent.tree = this.tree;
13717         overEvent.target = targetNode;
13718         overEvent.data = data;
13719         overEvent.point = pt;
13720         overEvent.source = dd;
13721         overEvent.rawEvent = e;
13722         overEvent.dropNode = dropNode;
13723         overEvent.cancel = false;  
13724         var result = this.tree.fireEvent("nodedragover", overEvent);
13725         return overEvent.cancel === false && result !== false;
13726     },
13727     
13728     getDropPoint : function(e, n, dd)
13729     {
13730         var tn = n.node;
13731         if(tn.isRoot){
13732             return tn.allowChildren !== false ? "append" : false; // always append for root
13733         }
13734         var dragEl = n.ddel;
13735         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
13736         var y = Roo.lib.Event.getPageY(e);
13737         //var noAppend = tn.allowChildren === false || tn.isLeaf();
13738         
13739         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
13740         var noAppend = tn.allowChildren === false;
13741         if(this.appendOnly || tn.parentNode.allowChildren === false){
13742             return noAppend ? false : "append";
13743         }
13744         var noBelow = false;
13745         if(!this.allowParentInsert){
13746             noBelow = tn.hasChildNodes() && tn.isExpanded();
13747         }
13748         var q = (b - t) / (noAppend ? 2 : 3);
13749         if(y >= t && y < (t + q)){
13750             return "above";
13751         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
13752             return "below";
13753         }else{
13754             return "append";
13755         }
13756     },
13757     
13758     onNodeEnter : function(n, dd, e, data)
13759     {
13760         this.cancelExpand();
13761     },
13762     
13763     onNodeOver : function(n, dd, e, data)
13764     {
13765        
13766         var pt = this.getDropPoint(e, n, dd);
13767         var node = n.node;
13768         
13769         // auto node expand check
13770         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
13771             this.queueExpand(node);
13772         }else if(pt != "append"){
13773             this.cancelExpand();
13774         }
13775         
13776         // set the insert point style on the target node
13777         var returnCls = this.dropNotAllowed;
13778         if(this.isValidDropPoint(n, pt, dd, e, data)){
13779            if(pt){
13780                var el = n.ddel;
13781                var cls;
13782                if(pt == "above"){
13783                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
13784                    cls = "x-tree-drag-insert-above";
13785                }else if(pt == "below"){
13786                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
13787                    cls = "x-tree-drag-insert-below";
13788                }else{
13789                    returnCls = "x-tree-drop-ok-append";
13790                    cls = "x-tree-drag-append";
13791                }
13792                if(this.lastInsertClass != cls){
13793                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
13794                    this.lastInsertClass = cls;
13795                }
13796            }
13797        }
13798        return returnCls;
13799     },
13800     
13801     onNodeOut : function(n, dd, e, data){
13802         
13803         this.cancelExpand();
13804         this.removeDropIndicators(n);
13805     },
13806     
13807     onNodeDrop : function(n, dd, e, data){
13808         var point = this.getDropPoint(e, n, dd);
13809         var targetNode = n.node;
13810         targetNode.ui.startDrop();
13811         if(!this.isValidDropPoint(n, point, dd, e, data)){
13812             targetNode.ui.endDrop();
13813             return false;
13814         }
13815         // first try to find the drop node
13816         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
13817         var dropEvent = {
13818             tree : this.tree,
13819             target: targetNode,
13820             data: data,
13821             point: point,
13822             source: dd,
13823             rawEvent: e,
13824             dropNode: dropNode,
13825             cancel: !dropNode   
13826         };
13827         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
13828         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
13829             targetNode.ui.endDrop();
13830             return false;
13831         }
13832         // allow target changing
13833         targetNode = dropEvent.target;
13834         if(point == "append" && !targetNode.isExpanded()){
13835             targetNode.expand(false, null, function(){
13836                 this.completeDrop(dropEvent);
13837             }.createDelegate(this));
13838         }else{
13839             this.completeDrop(dropEvent);
13840         }
13841         return true;
13842     },
13843     
13844     completeDrop : function(de){
13845         var ns = de.dropNode, p = de.point, t = de.target;
13846         if(!(ns instanceof Array)){
13847             ns = [ns];
13848         }
13849         var n;
13850         for(var i = 0, len = ns.length; i < len; i++){
13851             n = ns[i];
13852             if(p == "above"){
13853                 t.parentNode.insertBefore(n, t);
13854             }else if(p == "below"){
13855                 t.parentNode.insertBefore(n, t.nextSibling);
13856             }else{
13857                 t.appendChild(n);
13858             }
13859         }
13860         n.ui.focus();
13861         if(this.tree.hlDrop){
13862             n.ui.highlight();
13863         }
13864         t.ui.endDrop();
13865         this.tree.fireEvent("nodedrop", de);
13866     },
13867     
13868     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
13869         if(this.tree.hlDrop){
13870             dropNode.ui.focus();
13871             dropNode.ui.highlight();
13872         }
13873         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
13874     },
13875     
13876     getTree : function(){
13877         return this.tree;
13878     },
13879     
13880     removeDropIndicators : function(n){
13881         if(n && n.ddel){
13882             var el = n.ddel;
13883             Roo.fly(el).removeClass([
13884                     "x-tree-drag-insert-above",
13885                     "x-tree-drag-insert-below",
13886                     "x-tree-drag-append"]);
13887             this.lastInsertClass = "_noclass";
13888         }
13889     },
13890     
13891     beforeDragDrop : function(target, e, id){
13892         this.cancelExpand();
13893         return true;
13894     },
13895     
13896     afterRepair : function(data){
13897         if(data && Roo.enableFx){
13898             data.node.ui.highlight();
13899         }
13900         this.hideProxy();
13901     } 
13902     
13903 });
13904
13905 }
13906 /*
13907  * Based on:
13908  * Ext JS Library 1.1.1
13909  * Copyright(c) 2006-2007, Ext JS, LLC.
13910  *
13911  * Originally Released Under LGPL - original licence link has changed is not relivant.
13912  *
13913  * Fork - LGPL
13914  * <script type="text/javascript">
13915  */
13916  
13917
13918 if(Roo.dd.DragZone){
13919 Roo.tree.TreeDragZone = function(tree, config){
13920     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
13921     this.tree = tree;
13922 };
13923
13924 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
13925     ddGroup : "TreeDD",
13926    
13927     onBeforeDrag : function(data, e){
13928         var n = data.node;
13929         return n && n.draggable && !n.disabled;
13930     },
13931      
13932     
13933     onInitDrag : function(e){
13934         var data = this.dragData;
13935         this.tree.getSelectionModel().select(data.node);
13936         this.proxy.update("");
13937         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
13938         this.tree.fireEvent("startdrag", this.tree, data.node, e);
13939     },
13940     
13941     getRepairXY : function(e, data){
13942         return data.node.ui.getDDRepairXY();
13943     },
13944     
13945     onEndDrag : function(data, e){
13946         this.tree.fireEvent("enddrag", this.tree, data.node, e);
13947         
13948         
13949     },
13950     
13951     onValidDrop : function(dd, e, id){
13952         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
13953         this.hideProxy();
13954     },
13955     
13956     beforeInvalidDrop : function(e, id){
13957         // this scrolls the original position back into view
13958         var sm = this.tree.getSelectionModel();
13959         sm.clearSelections();
13960         sm.select(this.dragData.node);
13961     }
13962 });
13963 }/*
13964  * Based on:
13965  * Ext JS Library 1.1.1
13966  * Copyright(c) 2006-2007, Ext JS, LLC.
13967  *
13968  * Originally Released Under LGPL - original licence link has changed is not relivant.
13969  *
13970  * Fork - LGPL
13971  * <script type="text/javascript">
13972  */
13973 /**
13974  * @class Roo.tree.TreeEditor
13975  * @extends Roo.Editor
13976  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
13977  * as the editor field.
13978  * @constructor
13979  * @param {Object} config (used to be the tree panel.)
13980  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
13981  * 
13982  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
13983  * @cfg {Roo.form.TextField} field [required] The field configuration
13984  *
13985  * 
13986  */
13987 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
13988     var tree = config;
13989     var field;
13990     if (oldconfig) { // old style..
13991         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
13992     } else {
13993         // new style..
13994         tree = config.tree;
13995         config.field = config.field  || {};
13996         config.field.xtype = 'TextField';
13997         field = Roo.factory(config.field, Roo.form);
13998     }
13999     config = config || {};
14000     
14001     
14002     this.addEvents({
14003         /**
14004          * @event beforenodeedit
14005          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14006          * false from the handler of this event.
14007          * @param {Editor} this
14008          * @param {Roo.tree.Node} node 
14009          */
14010         "beforenodeedit" : true
14011     });
14012     
14013     //Roo.log(config);
14014     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14015
14016     this.tree = tree;
14017
14018     tree.on('beforeclick', this.beforeNodeClick, this);
14019     tree.getTreeEl().on('mousedown', this.hide, this);
14020     this.on('complete', this.updateNode, this);
14021     this.on('beforestartedit', this.fitToTree, this);
14022     this.on('startedit', this.bindScroll, this, {delay:10});
14023     this.on('specialkey', this.onSpecialKey, this);
14024 };
14025
14026 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14027     /**
14028      * @cfg {String} alignment
14029      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14030      */
14031     alignment: "l-l",
14032     // inherit
14033     autoSize: false,
14034     /**
14035      * @cfg {Boolean} hideEl
14036      * True to hide the bound element while the editor is displayed (defaults to false)
14037      */
14038     hideEl : false,
14039     /**
14040      * @cfg {String} cls
14041      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14042      */
14043     cls: "x-small-editor x-tree-editor",
14044     /**
14045      * @cfg {Boolean} shim
14046      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14047      */
14048     shim:false,
14049     // inherit
14050     shadow:"frame",
14051     /**
14052      * @cfg {Number} maxWidth
14053      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14054      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14055      * scroll and client offsets into account prior to each edit.
14056      */
14057     maxWidth: 250,
14058
14059     editDelay : 350,
14060
14061     // private
14062     fitToTree : function(ed, el){
14063         var td = this.tree.getTreeEl().dom, nd = el.dom;
14064         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14065             td.scrollLeft = nd.offsetLeft;
14066         }
14067         var w = Math.min(
14068                 this.maxWidth,
14069                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14070         this.setSize(w, '');
14071         
14072         return this.fireEvent('beforenodeedit', this, this.editNode);
14073         
14074     },
14075
14076     // private
14077     triggerEdit : function(node){
14078         this.completeEdit();
14079         this.editNode = node;
14080         this.startEdit(node.ui.textNode, node.text);
14081     },
14082
14083     // private
14084     bindScroll : function(){
14085         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14086     },
14087
14088     // private
14089     beforeNodeClick : function(node, e){
14090         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14091         this.lastClick = new Date();
14092         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14093             e.stopEvent();
14094             this.triggerEdit(node);
14095             return false;
14096         }
14097         return true;
14098     },
14099
14100     // private
14101     updateNode : function(ed, value){
14102         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14103         this.editNode.setText(value);
14104     },
14105
14106     // private
14107     onHide : function(){
14108         Roo.tree.TreeEditor.superclass.onHide.call(this);
14109         if(this.editNode){
14110             this.editNode.ui.focus();
14111         }
14112     },
14113
14114     // private
14115     onSpecialKey : function(field, e){
14116         var k = e.getKey();
14117         if(k == e.ESC){
14118             e.stopEvent();
14119             this.cancelEdit();
14120         }else if(k == e.ENTER && !e.hasModifier()){
14121             e.stopEvent();
14122             this.completeEdit();
14123         }
14124     }
14125 });//<Script type="text/javascript">
14126 /*
14127  * Based on:
14128  * Ext JS Library 1.1.1
14129  * Copyright(c) 2006-2007, Ext JS, LLC.
14130  *
14131  * Originally Released Under LGPL - original licence link has changed is not relivant.
14132  *
14133  * Fork - LGPL
14134  * <script type="text/javascript">
14135  */
14136  
14137 /**
14138  * Not documented??? - probably should be...
14139  */
14140
14141 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14142     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14143     
14144     renderElements : function(n, a, targetNode, bulkRender){
14145         //consel.log("renderElements?");
14146         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14147
14148         var t = n.getOwnerTree();
14149         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14150         
14151         var cols = t.columns;
14152         var bw = t.borderWidth;
14153         var c = cols[0];
14154         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14155          var cb = typeof a.checked == "boolean";
14156         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14157         var colcls = 'x-t-' + tid + '-c0';
14158         var buf = [
14159             '<li class="x-tree-node">',
14160             
14161                 
14162                 '<div class="x-tree-node-el ', a.cls,'">',
14163                     // extran...
14164                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14165                 
14166                 
14167                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14168                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14169                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14170                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14171                            (a.iconCls ? ' '+a.iconCls : ''),
14172                            '" unselectable="on" />',
14173                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14174                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14175                              
14176                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14177                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14178                             '<span unselectable="on" qtip="' + tx + '">',
14179                              tx,
14180                              '</span></a>' ,
14181                     '</div>',
14182                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14183                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14184                  ];
14185         for(var i = 1, len = cols.length; i < len; i++){
14186             c = cols[i];
14187             colcls = 'x-t-' + tid + '-c' +i;
14188             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14189             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14190                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14191                       "</div>");
14192          }
14193          
14194          buf.push(
14195             '</a>',
14196             '<div class="x-clear"></div></div>',
14197             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14198             "</li>");
14199         
14200         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14201             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14202                                 n.nextSibling.ui.getEl(), buf.join(""));
14203         }else{
14204             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14205         }
14206         var el = this.wrap.firstChild;
14207         this.elRow = el;
14208         this.elNode = el.firstChild;
14209         this.ranchor = el.childNodes[1];
14210         this.ctNode = this.wrap.childNodes[1];
14211         var cs = el.firstChild.childNodes;
14212         this.indentNode = cs[0];
14213         this.ecNode = cs[1];
14214         this.iconNode = cs[2];
14215         var index = 3;
14216         if(cb){
14217             this.checkbox = cs[3];
14218             index++;
14219         }
14220         this.anchor = cs[index];
14221         
14222         this.textNode = cs[index].firstChild;
14223         
14224         //el.on("click", this.onClick, this);
14225         //el.on("dblclick", this.onDblClick, this);
14226         
14227         
14228        // console.log(this);
14229     },
14230     initEvents : function(){
14231         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14232         
14233             
14234         var a = this.ranchor;
14235
14236         var el = Roo.get(a);
14237
14238         if(Roo.isOpera){ // opera render bug ignores the CSS
14239             el.setStyle("text-decoration", "none");
14240         }
14241
14242         el.on("click", this.onClick, this);
14243         el.on("dblclick", this.onDblClick, this);
14244         el.on("contextmenu", this.onContextMenu, this);
14245         
14246     },
14247     
14248     /*onSelectedChange : function(state){
14249         if(state){
14250             this.focus();
14251             this.addClass("x-tree-selected");
14252         }else{
14253             //this.blur();
14254             this.removeClass("x-tree-selected");
14255         }
14256     },*/
14257     addClass : function(cls){
14258         if(this.elRow){
14259             Roo.fly(this.elRow).addClass(cls);
14260         }
14261         
14262     },
14263     
14264     
14265     removeClass : function(cls){
14266         if(this.elRow){
14267             Roo.fly(this.elRow).removeClass(cls);
14268         }
14269     }
14270
14271     
14272     
14273 });//<Script type="text/javascript">
14274
14275 /*
14276  * Based on:
14277  * Ext JS Library 1.1.1
14278  * Copyright(c) 2006-2007, Ext JS, LLC.
14279  *
14280  * Originally Released Under LGPL - original licence link has changed is not relivant.
14281  *
14282  * Fork - LGPL
14283  * <script type="text/javascript">
14284  */
14285  
14286
14287 /**
14288  * @class Roo.tree.ColumnTree
14289  * @extends Roo.tree.TreePanel
14290  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14291  * @cfg {int} borderWidth  compined right/left border allowance
14292  * @constructor
14293  * @param {String/HTMLElement/Element} el The container element
14294  * @param {Object} config
14295  */
14296 Roo.tree.ColumnTree =  function(el, config)
14297 {
14298    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14299    this.addEvents({
14300         /**
14301         * @event resize
14302         * Fire this event on a container when it resizes
14303         * @param {int} w Width
14304         * @param {int} h Height
14305         */
14306        "resize" : true
14307     });
14308     this.on('resize', this.onResize, this);
14309 };
14310
14311 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14312     //lines:false,
14313     
14314     
14315     borderWidth: Roo.isBorderBox ? 0 : 2, 
14316     headEls : false,
14317     
14318     render : function(){
14319         // add the header.....
14320        
14321         Roo.tree.ColumnTree.superclass.render.apply(this);
14322         
14323         this.el.addClass('x-column-tree');
14324         
14325         this.headers = this.el.createChild(
14326             {cls:'x-tree-headers'},this.innerCt.dom);
14327    
14328         var cols = this.columns, c;
14329         var totalWidth = 0;
14330         this.headEls = [];
14331         var  len = cols.length;
14332         for(var i = 0; i < len; i++){
14333              c = cols[i];
14334              totalWidth += c.width;
14335             this.headEls.push(this.headers.createChild({
14336                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14337                  cn: {
14338                      cls:'x-tree-hd-text',
14339                      html: c.header
14340                  },
14341                  style:'width:'+(c.width-this.borderWidth)+'px;'
14342              }));
14343         }
14344         this.headers.createChild({cls:'x-clear'});
14345         // prevent floats from wrapping when clipped
14346         this.headers.setWidth(totalWidth);
14347         //this.innerCt.setWidth(totalWidth);
14348         this.innerCt.setStyle({ overflow: 'auto' });
14349         this.onResize(this.width, this.height);
14350              
14351         
14352     },
14353     onResize : function(w,h)
14354     {
14355         this.height = h;
14356         this.width = w;
14357         // resize cols..
14358         this.innerCt.setWidth(this.width);
14359         this.innerCt.setHeight(this.height-20);
14360         
14361         // headers...
14362         var cols = this.columns, c;
14363         var totalWidth = 0;
14364         var expEl = false;
14365         var len = cols.length;
14366         for(var i = 0; i < len; i++){
14367             c = cols[i];
14368             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14369                 // it's the expander..
14370                 expEl  = this.headEls[i];
14371                 continue;
14372             }
14373             totalWidth += c.width;
14374             
14375         }
14376         if (expEl) {
14377             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14378         }
14379         this.headers.setWidth(w-20);
14380
14381         
14382         
14383         
14384     }
14385 });
14386 /*
14387  * Based on:
14388  * Ext JS Library 1.1.1
14389  * Copyright(c) 2006-2007, Ext JS, LLC.
14390  *
14391  * Originally Released Under LGPL - original licence link has changed is not relivant.
14392  *
14393  * Fork - LGPL
14394  * <script type="text/javascript">
14395  */
14396  
14397 /**
14398  * @class Roo.menu.Menu
14399  * @extends Roo.util.Observable
14400  * @children Roo.menu.BaseItem
14401  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14402  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14403  * @constructor
14404  * Creates a new Menu
14405  * @param {Object} config Configuration options
14406  */
14407 Roo.menu.Menu = function(config){
14408     
14409     Roo.menu.Menu.superclass.constructor.call(this, config);
14410     
14411     this.id = this.id || Roo.id();
14412     this.addEvents({
14413         /**
14414          * @event beforeshow
14415          * Fires before this menu is displayed
14416          * @param {Roo.menu.Menu} this
14417          */
14418         beforeshow : true,
14419         /**
14420          * @event beforehide
14421          * Fires before this menu is hidden
14422          * @param {Roo.menu.Menu} this
14423          */
14424         beforehide : true,
14425         /**
14426          * @event show
14427          * Fires after this menu is displayed
14428          * @param {Roo.menu.Menu} this
14429          */
14430         show : true,
14431         /**
14432          * @event hide
14433          * Fires after this menu is hidden
14434          * @param {Roo.menu.Menu} this
14435          */
14436         hide : true,
14437         /**
14438          * @event click
14439          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14440          * @param {Roo.menu.Menu} this
14441          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14442          * @param {Roo.EventObject} e
14443          */
14444         click : true,
14445         /**
14446          * @event mouseover
14447          * Fires when the mouse is hovering over this menu
14448          * @param {Roo.menu.Menu} this
14449          * @param {Roo.EventObject} e
14450          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14451          */
14452         mouseover : true,
14453         /**
14454          * @event mouseout
14455          * Fires when the mouse exits this menu
14456          * @param {Roo.menu.Menu} this
14457          * @param {Roo.EventObject} e
14458          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14459          */
14460         mouseout : true,
14461         /**
14462          * @event itemclick
14463          * Fires when a menu item contained in this menu is clicked
14464          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14465          * @param {Roo.EventObject} e
14466          */
14467         itemclick: true
14468     });
14469     if (this.registerMenu) {
14470         Roo.menu.MenuMgr.register(this);
14471     }
14472     
14473     var mis = this.items;
14474     this.items = new Roo.util.MixedCollection();
14475     if(mis){
14476         this.add.apply(this, mis);
14477     }
14478 };
14479
14480 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14481     /**
14482      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14483      */
14484     minWidth : 120,
14485     /**
14486      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14487      * for bottom-right shadow (defaults to "sides")
14488      */
14489     shadow : "sides",
14490     /**
14491      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14492      * this menu (defaults to "tl-tr?")
14493      */
14494     subMenuAlign : "tl-tr?",
14495     /**
14496      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14497      * relative to its element of origin (defaults to "tl-bl?")
14498      */
14499     defaultAlign : "tl-bl?",
14500     /**
14501      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14502      */
14503     allowOtherMenus : false,
14504     /**
14505      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14506      */
14507     registerMenu : true,
14508
14509     hidden:true,
14510
14511     // private
14512     render : function(){
14513         if(this.el){
14514             return;
14515         }
14516         var el = this.el = new Roo.Layer({
14517             cls: "x-menu",
14518             shadow:this.shadow,
14519             constrain: false,
14520             parentEl: this.parentEl || document.body,
14521             zindex:15000
14522         });
14523
14524         this.keyNav = new Roo.menu.MenuNav(this);
14525
14526         if(this.plain){
14527             el.addClass("x-menu-plain");
14528         }
14529         if(this.cls){
14530             el.addClass(this.cls);
14531         }
14532         // generic focus element
14533         this.focusEl = el.createChild({
14534             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14535         });
14536         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14537         //disabling touch- as it's causing issues ..
14538         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14539         ul.on('click'   , this.onClick, this);
14540         
14541         
14542         ul.on("mouseover", this.onMouseOver, this);
14543         ul.on("mouseout", this.onMouseOut, this);
14544         this.items.each(function(item){
14545             if (item.hidden) {
14546                 return;
14547             }
14548             
14549             var li = document.createElement("li");
14550             li.className = "x-menu-list-item";
14551             ul.dom.appendChild(li);
14552             item.render(li, this);
14553         }, this);
14554         this.ul = ul;
14555         this.autoWidth();
14556     },
14557
14558     // private
14559     autoWidth : function(){
14560         var el = this.el, ul = this.ul;
14561         if(!el){
14562             return;
14563         }
14564         var w = this.width;
14565         if(w){
14566             el.setWidth(w);
14567         }else if(Roo.isIE){
14568             el.setWidth(this.minWidth);
14569             var t = el.dom.offsetWidth; // force recalc
14570             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14571         }
14572     },
14573
14574     // private
14575     delayAutoWidth : function(){
14576         if(this.rendered){
14577             if(!this.awTask){
14578                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14579             }
14580             this.awTask.delay(20);
14581         }
14582     },
14583
14584     // private
14585     findTargetItem : function(e){
14586         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14587         if(t && t.menuItemId){
14588             return this.items.get(t.menuItemId);
14589         }
14590     },
14591
14592     // private
14593     onClick : function(e){
14594         Roo.log("menu.onClick");
14595         var t = this.findTargetItem(e);
14596         if(!t){
14597             return;
14598         }
14599         Roo.log(e);
14600         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14601             if(t == this.activeItem && t.shouldDeactivate(e)){
14602                 this.activeItem.deactivate();
14603                 delete this.activeItem;
14604                 return;
14605             }
14606             if(t.canActivate){
14607                 this.setActiveItem(t, true);
14608             }
14609             return;
14610             
14611             
14612         }
14613         
14614         t.onClick(e);
14615         this.fireEvent("click", this, t, e);
14616     },
14617
14618     // private
14619     setActiveItem : function(item, autoExpand){
14620         if(item != this.activeItem){
14621             if(this.activeItem){
14622                 this.activeItem.deactivate();
14623             }
14624             this.activeItem = item;
14625             item.activate(autoExpand);
14626         }else if(autoExpand){
14627             item.expandMenu();
14628         }
14629     },
14630
14631     // private
14632     tryActivate : function(start, step){
14633         var items = this.items;
14634         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
14635             var item = items.get(i);
14636             if(!item.disabled && item.canActivate){
14637                 this.setActiveItem(item, false);
14638                 return item;
14639             }
14640         }
14641         return false;
14642     },
14643
14644     // private
14645     onMouseOver : function(e){
14646         var t;
14647         if(t = this.findTargetItem(e)){
14648             if(t.canActivate && !t.disabled){
14649                 this.setActiveItem(t, true);
14650             }
14651         }
14652         this.fireEvent("mouseover", this, e, t);
14653     },
14654
14655     // private
14656     onMouseOut : function(e){
14657         var t;
14658         if(t = this.findTargetItem(e)){
14659             if(t == this.activeItem && t.shouldDeactivate(e)){
14660                 this.activeItem.deactivate();
14661                 delete this.activeItem;
14662             }
14663         }
14664         this.fireEvent("mouseout", this, e, t);
14665     },
14666
14667     /**
14668      * Read-only.  Returns true if the menu is currently displayed, else false.
14669      * @type Boolean
14670      */
14671     isVisible : function(){
14672         return this.el && !this.hidden;
14673     },
14674
14675     /**
14676      * Displays this menu relative to another element
14677      * @param {String/HTMLElement/Roo.Element} element The element to align to
14678      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
14679      * the element (defaults to this.defaultAlign)
14680      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14681      */
14682     show : function(el, pos, parentMenu){
14683         this.parentMenu = parentMenu;
14684         if(!this.el){
14685             this.render();
14686         }
14687         this.fireEvent("beforeshow", this);
14688         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
14689     },
14690
14691     /**
14692      * Displays this menu at a specific xy position
14693      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
14694      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14695      */
14696     showAt : function(xy, parentMenu, /* private: */_e){
14697         this.parentMenu = parentMenu;
14698         if(!this.el){
14699             this.render();
14700         }
14701         if(_e !== false){
14702             this.fireEvent("beforeshow", this);
14703             xy = this.el.adjustForConstraints(xy);
14704         }
14705         this.el.setXY(xy);
14706         this.el.show();
14707         this.hidden = false;
14708         this.focus();
14709         this.fireEvent("show", this);
14710     },
14711
14712     focus : function(){
14713         if(!this.hidden){
14714             this.doFocus.defer(50, this);
14715         }
14716     },
14717
14718     doFocus : function(){
14719         if(!this.hidden){
14720             this.focusEl.focus();
14721         }
14722     },
14723
14724     /**
14725      * Hides this menu and optionally all parent menus
14726      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
14727      */
14728     hide : function(deep){
14729         if(this.el && this.isVisible()){
14730             this.fireEvent("beforehide", this);
14731             if(this.activeItem){
14732                 this.activeItem.deactivate();
14733                 this.activeItem = null;
14734             }
14735             this.el.hide();
14736             this.hidden = true;
14737             this.fireEvent("hide", this);
14738         }
14739         if(deep === true && this.parentMenu){
14740             this.parentMenu.hide(true);
14741         }
14742     },
14743
14744     /**
14745      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
14746      * Any of the following are valid:
14747      * <ul>
14748      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
14749      * <li>An HTMLElement object which will be converted to a menu item</li>
14750      * <li>A menu item config object that will be created as a new menu item</li>
14751      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
14752      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
14753      * </ul>
14754      * Usage:
14755      * <pre><code>
14756 // Create the menu
14757 var menu = new Roo.menu.Menu();
14758
14759 // Create a menu item to add by reference
14760 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
14761
14762 // Add a bunch of items at once using different methods.
14763 // Only the last item added will be returned.
14764 var item = menu.add(
14765     menuItem,                // add existing item by ref
14766     'Dynamic Item',          // new TextItem
14767     '-',                     // new separator
14768     { text: 'Config Item' }  // new item by config
14769 );
14770 </code></pre>
14771      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
14772      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
14773      */
14774     add : function(){
14775         var a = arguments, l = a.length, item;
14776         for(var i = 0; i < l; i++){
14777             var el = a[i];
14778             if ((typeof(el) == "object") && el.xtype && el.xns) {
14779                 el = Roo.factory(el, Roo.menu);
14780             }
14781             
14782             if(el.render){ // some kind of Item
14783                 item = this.addItem(el);
14784             }else if(typeof el == "string"){ // string
14785                 if(el == "separator" || el == "-"){
14786                     item = this.addSeparator();
14787                 }else{
14788                     item = this.addText(el);
14789                 }
14790             }else if(el.tagName || el.el){ // element
14791                 item = this.addElement(el);
14792             }else if(typeof el == "object"){ // must be menu item config?
14793                 item = this.addMenuItem(el);
14794             }
14795         }
14796         return item;
14797     },
14798
14799     /**
14800      * Returns this menu's underlying {@link Roo.Element} object
14801      * @return {Roo.Element} The element
14802      */
14803     getEl : function(){
14804         if(!this.el){
14805             this.render();
14806         }
14807         return this.el;
14808     },
14809
14810     /**
14811      * Adds a separator bar to the menu
14812      * @return {Roo.menu.Item} The menu item that was added
14813      */
14814     addSeparator : function(){
14815         return this.addItem(new Roo.menu.Separator());
14816     },
14817
14818     /**
14819      * Adds an {@link Roo.Element} object to the menu
14820      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
14821      * @return {Roo.menu.Item} The menu item that was added
14822      */
14823     addElement : function(el){
14824         return this.addItem(new Roo.menu.BaseItem(el));
14825     },
14826
14827     /**
14828      * Adds an existing object based on {@link Roo.menu.Item} to the menu
14829      * @param {Roo.menu.Item} item The menu item to add
14830      * @return {Roo.menu.Item} The menu item that was added
14831      */
14832     addItem : function(item){
14833         this.items.add(item);
14834         if(this.ul){
14835             var li = document.createElement("li");
14836             li.className = "x-menu-list-item";
14837             this.ul.dom.appendChild(li);
14838             item.render(li, this);
14839             this.delayAutoWidth();
14840         }
14841         return item;
14842     },
14843
14844     /**
14845      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
14846      * @param {Object} config A MenuItem config object
14847      * @return {Roo.menu.Item} The menu item that was added
14848      */
14849     addMenuItem : function(config){
14850         if(!(config instanceof Roo.menu.Item)){
14851             if(typeof config.checked == "boolean"){ // must be check menu item config?
14852                 config = new Roo.menu.CheckItem(config);
14853             }else{
14854                 config = new Roo.menu.Item(config);
14855             }
14856         }
14857         return this.addItem(config);
14858     },
14859
14860     /**
14861      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
14862      * @param {String} text The text to display in the menu item
14863      * @return {Roo.menu.Item} The menu item that was added
14864      */
14865     addText : function(text){
14866         return this.addItem(new Roo.menu.TextItem({ text : text }));
14867     },
14868
14869     /**
14870      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
14871      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
14872      * @param {Roo.menu.Item} item The menu item to add
14873      * @return {Roo.menu.Item} The menu item that was added
14874      */
14875     insert : function(index, item){
14876         this.items.insert(index, item);
14877         if(this.ul){
14878             var li = document.createElement("li");
14879             li.className = "x-menu-list-item";
14880             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
14881             item.render(li, this);
14882             this.delayAutoWidth();
14883         }
14884         return item;
14885     },
14886
14887     /**
14888      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
14889      * @param {Roo.menu.Item} item The menu item to remove
14890      */
14891     remove : function(item){
14892         this.items.removeKey(item.id);
14893         item.destroy();
14894     },
14895
14896     /**
14897      * Removes and destroys all items in the menu
14898      */
14899     removeAll : function(){
14900         var f;
14901         while(f = this.items.first()){
14902             this.remove(f);
14903         }
14904     }
14905 });
14906
14907 // MenuNav is a private utility class used internally by the Menu
14908 Roo.menu.MenuNav = function(menu){
14909     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
14910     this.scope = this.menu = menu;
14911 };
14912
14913 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
14914     doRelay : function(e, h){
14915         var k = e.getKey();
14916         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
14917             this.menu.tryActivate(0, 1);
14918             return false;
14919         }
14920         return h.call(this.scope || this, e, this.menu);
14921     },
14922
14923     up : function(e, m){
14924         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
14925             m.tryActivate(m.items.length-1, -1);
14926         }
14927     },
14928
14929     down : function(e, m){
14930         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
14931             m.tryActivate(0, 1);
14932         }
14933     },
14934
14935     right : function(e, m){
14936         if(m.activeItem){
14937             m.activeItem.expandMenu(true);
14938         }
14939     },
14940
14941     left : function(e, m){
14942         m.hide();
14943         if(m.parentMenu && m.parentMenu.activeItem){
14944             m.parentMenu.activeItem.activate();
14945         }
14946     },
14947
14948     enter : function(e, m){
14949         if(m.activeItem){
14950             e.stopPropagation();
14951             m.activeItem.onClick(e);
14952             m.fireEvent("click", this, m.activeItem);
14953             return true;
14954         }
14955     }
14956 });/*
14957  * Based on:
14958  * Ext JS Library 1.1.1
14959  * Copyright(c) 2006-2007, Ext JS, LLC.
14960  *
14961  * Originally Released Under LGPL - original licence link has changed is not relivant.
14962  *
14963  * Fork - LGPL
14964  * <script type="text/javascript">
14965  */
14966  
14967 /**
14968  * @class Roo.menu.MenuMgr
14969  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
14970  * @singleton
14971  */
14972 Roo.menu.MenuMgr = function(){
14973    var menus, active, groups = {}, attached = false, lastShow = new Date();
14974
14975    // private - called when first menu is created
14976    function init(){
14977        menus = {};
14978        active = new Roo.util.MixedCollection();
14979        Roo.get(document).addKeyListener(27, function(){
14980            if(active.length > 0){
14981                hideAll();
14982            }
14983        });
14984    }
14985
14986    // private
14987    function hideAll(){
14988        if(active && active.length > 0){
14989            var c = active.clone();
14990            c.each(function(m){
14991                m.hide();
14992            });
14993        }
14994    }
14995
14996    // private
14997    function onHide(m){
14998        active.remove(m);
14999        if(active.length < 1){
15000            Roo.get(document).un("mousedown", onMouseDown);
15001            attached = false;
15002        }
15003    }
15004
15005    // private
15006    function onShow(m){
15007        var last = active.last();
15008        lastShow = new Date();
15009        active.add(m);
15010        if(!attached){
15011            Roo.get(document).on("mousedown", onMouseDown);
15012            attached = true;
15013        }
15014        if(m.parentMenu){
15015           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15016           m.parentMenu.activeChild = m;
15017        }else if(last && last.isVisible()){
15018           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15019        }
15020    }
15021
15022    // private
15023    function onBeforeHide(m){
15024        if(m.activeChild){
15025            m.activeChild.hide();
15026        }
15027        if(m.autoHideTimer){
15028            clearTimeout(m.autoHideTimer);
15029            delete m.autoHideTimer;
15030        }
15031    }
15032
15033    // private
15034    function onBeforeShow(m){
15035        var pm = m.parentMenu;
15036        if(!pm && !m.allowOtherMenus){
15037            hideAll();
15038        }else if(pm && pm.activeChild && active != m){
15039            pm.activeChild.hide();
15040        }
15041    }
15042
15043    // private
15044    function onMouseDown(e){
15045        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15046            hideAll();
15047        }
15048    }
15049
15050    // private
15051    function onBeforeCheck(mi, state){
15052        if(state){
15053            var g = groups[mi.group];
15054            for(var i = 0, l = g.length; i < l; i++){
15055                if(g[i] != mi){
15056                    g[i].setChecked(false);
15057                }
15058            }
15059        }
15060    }
15061
15062    return {
15063
15064        /**
15065         * Hides all menus that are currently visible
15066         */
15067        hideAll : function(){
15068             hideAll();  
15069        },
15070
15071        // private
15072        register : function(menu){
15073            if(!menus){
15074                init();
15075            }
15076            menus[menu.id] = menu;
15077            menu.on("beforehide", onBeforeHide);
15078            menu.on("hide", onHide);
15079            menu.on("beforeshow", onBeforeShow);
15080            menu.on("show", onShow);
15081            var g = menu.group;
15082            if(g && menu.events["checkchange"]){
15083                if(!groups[g]){
15084                    groups[g] = [];
15085                }
15086                groups[g].push(menu);
15087                menu.on("checkchange", onCheck);
15088            }
15089        },
15090
15091         /**
15092          * Returns a {@link Roo.menu.Menu} object
15093          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15094          * be used to generate and return a new Menu instance.
15095          */
15096        get : function(menu){
15097            if(typeof menu == "string"){ // menu id
15098                return menus[menu];
15099            }else if(menu.events){  // menu instance
15100                return menu;
15101            }else if(typeof menu.length == 'number'){ // array of menu items?
15102                return new Roo.menu.Menu({items:menu});
15103            }else{ // otherwise, must be a config
15104                return new Roo.menu.Menu(menu);
15105            }
15106        },
15107
15108        // private
15109        unregister : function(menu){
15110            delete menus[menu.id];
15111            menu.un("beforehide", onBeforeHide);
15112            menu.un("hide", onHide);
15113            menu.un("beforeshow", onBeforeShow);
15114            menu.un("show", onShow);
15115            var g = menu.group;
15116            if(g && menu.events["checkchange"]){
15117                groups[g].remove(menu);
15118                menu.un("checkchange", onCheck);
15119            }
15120        },
15121
15122        // private
15123        registerCheckable : function(menuItem){
15124            var g = menuItem.group;
15125            if(g){
15126                if(!groups[g]){
15127                    groups[g] = [];
15128                }
15129                groups[g].push(menuItem);
15130                menuItem.on("beforecheckchange", onBeforeCheck);
15131            }
15132        },
15133
15134        // private
15135        unregisterCheckable : function(menuItem){
15136            var g = menuItem.group;
15137            if(g){
15138                groups[g].remove(menuItem);
15139                menuItem.un("beforecheckchange", onBeforeCheck);
15140            }
15141        }
15142    };
15143 }();/*
15144  * Based on:
15145  * Ext JS Library 1.1.1
15146  * Copyright(c) 2006-2007, Ext JS, LLC.
15147  *
15148  * Originally Released Under LGPL - original licence link has changed is not relivant.
15149  *
15150  * Fork - LGPL
15151  * <script type="text/javascript">
15152  */
15153  
15154
15155 /**
15156  * @class Roo.menu.BaseItem
15157  * @extends Roo.Component
15158  * @abstract
15159  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15160  * management and base configuration options shared by all menu components.
15161  * @constructor
15162  * Creates a new BaseItem
15163  * @param {Object} config Configuration options
15164  */
15165 Roo.menu.BaseItem = function(config){
15166     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15167
15168     this.addEvents({
15169         /**
15170          * @event click
15171          * Fires when this item is clicked
15172          * @param {Roo.menu.BaseItem} this
15173          * @param {Roo.EventObject} e
15174          */
15175         click: true,
15176         /**
15177          * @event activate
15178          * Fires when this item is activated
15179          * @param {Roo.menu.BaseItem} this
15180          */
15181         activate : true,
15182         /**
15183          * @event deactivate
15184          * Fires when this item is deactivated
15185          * @param {Roo.menu.BaseItem} this
15186          */
15187         deactivate : true
15188     });
15189
15190     if(this.handler){
15191         this.on("click", this.handler, this.scope, true);
15192     }
15193 };
15194
15195 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15196     /**
15197      * @cfg {Function} handler
15198      * A function that will handle the click event of this menu item (defaults to undefined)
15199      */
15200     /**
15201      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15202      */
15203     canActivate : false,
15204     
15205      /**
15206      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15207      */
15208     hidden: false,
15209     
15210     /**
15211      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15212      */
15213     activeClass : "x-menu-item-active",
15214     /**
15215      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15216      */
15217     hideOnClick : true,
15218     /**
15219      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15220      */
15221     hideDelay : 100,
15222
15223     // private
15224     ctype: "Roo.menu.BaseItem",
15225
15226     // private
15227     actionMode : "container",
15228
15229     // private
15230     render : function(container, parentMenu){
15231         this.parentMenu = parentMenu;
15232         Roo.menu.BaseItem.superclass.render.call(this, container);
15233         this.container.menuItemId = this.id;
15234     },
15235
15236     // private
15237     onRender : function(container, position){
15238         this.el = Roo.get(this.el);
15239         container.dom.appendChild(this.el.dom);
15240     },
15241
15242     // private
15243     onClick : function(e){
15244         if(!this.disabled && this.fireEvent("click", this, e) !== false
15245                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15246             this.handleClick(e);
15247         }else{
15248             e.stopEvent();
15249         }
15250     },
15251
15252     // private
15253     activate : function(){
15254         if(this.disabled){
15255             return false;
15256         }
15257         var li = this.container;
15258         li.addClass(this.activeClass);
15259         this.region = li.getRegion().adjust(2, 2, -2, -2);
15260         this.fireEvent("activate", this);
15261         return true;
15262     },
15263
15264     // private
15265     deactivate : function(){
15266         this.container.removeClass(this.activeClass);
15267         this.fireEvent("deactivate", this);
15268     },
15269
15270     // private
15271     shouldDeactivate : function(e){
15272         return !this.region || !this.region.contains(e.getPoint());
15273     },
15274
15275     // private
15276     handleClick : function(e){
15277         if(this.hideOnClick){
15278             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15279         }
15280     },
15281
15282     // private
15283     expandMenu : function(autoActivate){
15284         // do nothing
15285     },
15286
15287     // private
15288     hideMenu : function(){
15289         // do nothing
15290     }
15291 });/*
15292  * Based on:
15293  * Ext JS Library 1.1.1
15294  * Copyright(c) 2006-2007, Ext JS, LLC.
15295  *
15296  * Originally Released Under LGPL - original licence link has changed is not relivant.
15297  *
15298  * Fork - LGPL
15299  * <script type="text/javascript">
15300  */
15301  
15302 /**
15303  * @class Roo.menu.Adapter
15304  * @extends Roo.menu.BaseItem
15305  * @abstract
15306  * A base utility class that adapts a non-menu component so that it can be wrapped by a menu item and added to a menu.
15307  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15308  * @constructor
15309  * Creates a new Adapter
15310  * @param {Object} config Configuration options
15311  */
15312 Roo.menu.Adapter = function(component, config){
15313     Roo.menu.Adapter.superclass.constructor.call(this, config);
15314     this.component = component;
15315 };
15316 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15317     // private
15318     canActivate : true,
15319
15320     // private
15321     onRender : function(container, position){
15322         this.component.render(container);
15323         this.el = this.component.getEl();
15324     },
15325
15326     // private
15327     activate : function(){
15328         if(this.disabled){
15329             return false;
15330         }
15331         this.component.focus();
15332         this.fireEvent("activate", this);
15333         return true;
15334     },
15335
15336     // private
15337     deactivate : function(){
15338         this.fireEvent("deactivate", this);
15339     },
15340
15341     // private
15342     disable : function(){
15343         this.component.disable();
15344         Roo.menu.Adapter.superclass.disable.call(this);
15345     },
15346
15347     // private
15348     enable : function(){
15349         this.component.enable();
15350         Roo.menu.Adapter.superclass.enable.call(this);
15351     }
15352 });/*
15353  * Based on:
15354  * Ext JS Library 1.1.1
15355  * Copyright(c) 2006-2007, Ext JS, LLC.
15356  *
15357  * Originally Released Under LGPL - original licence link has changed is not relivant.
15358  *
15359  * Fork - LGPL
15360  * <script type="text/javascript">
15361  */
15362
15363 /**
15364  * @class Roo.menu.TextItem
15365  * @extends Roo.menu.BaseItem
15366  * Adds a static text string to a menu, usually used as either a heading or group separator.
15367  * Note: old style constructor with text is still supported.
15368  * 
15369  * @constructor
15370  * Creates a new TextItem
15371  * @param {Object} cfg Configuration
15372  */
15373 Roo.menu.TextItem = function(cfg){
15374     if (typeof(cfg) == 'string') {
15375         this.text = cfg;
15376     } else {
15377         Roo.apply(this,cfg);
15378     }
15379     
15380     Roo.menu.TextItem.superclass.constructor.call(this);
15381 };
15382
15383 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15384     /**
15385      * @cfg {String} text Text to show on item.
15386      */
15387     text : '',
15388     
15389     /**
15390      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15391      */
15392     hideOnClick : false,
15393     /**
15394      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15395      */
15396     itemCls : "x-menu-text",
15397
15398     // private
15399     onRender : function(){
15400         var s = document.createElement("span");
15401         s.className = this.itemCls;
15402         s.innerHTML = this.text;
15403         this.el = s;
15404         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15405     }
15406 });/*
15407  * Based on:
15408  * Ext JS Library 1.1.1
15409  * Copyright(c) 2006-2007, Ext JS, LLC.
15410  *
15411  * Originally Released Under LGPL - original licence link has changed is not relivant.
15412  *
15413  * Fork - LGPL
15414  * <script type="text/javascript">
15415  */
15416
15417 /**
15418  * @class Roo.menu.Separator
15419  * @extends Roo.menu.BaseItem
15420  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15421  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15422  * @constructor
15423  * @param {Object} config Configuration options
15424  */
15425 Roo.menu.Separator = function(config){
15426     Roo.menu.Separator.superclass.constructor.call(this, config);
15427 };
15428
15429 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15430     /**
15431      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15432      */
15433     itemCls : "x-menu-sep",
15434     /**
15435      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15436      */
15437     hideOnClick : false,
15438
15439     // private
15440     onRender : function(li){
15441         var s = document.createElement("span");
15442         s.className = this.itemCls;
15443         s.innerHTML = "&#160;";
15444         this.el = s;
15445         li.addClass("x-menu-sep-li");
15446         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15447     }
15448 });/*
15449  * Based on:
15450  * Ext JS Library 1.1.1
15451  * Copyright(c) 2006-2007, Ext JS, LLC.
15452  *
15453  * Originally Released Under LGPL - original licence link has changed is not relivant.
15454  *
15455  * Fork - LGPL
15456  * <script type="text/javascript">
15457  */
15458 /**
15459  * @class Roo.menu.Item
15460  * @extends Roo.menu.BaseItem
15461  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15462  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15463  * activation and click handling.
15464  * @constructor
15465  * Creates a new Item
15466  * @param {Object} config Configuration options
15467  */
15468 Roo.menu.Item = function(config){
15469     Roo.menu.Item.superclass.constructor.call(this, config);
15470     if(this.menu){
15471         this.menu = Roo.menu.MenuMgr.get(this.menu);
15472     }
15473 };
15474 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15475     /**
15476      * @cfg {Roo.menu.Menu} menu
15477      * A Sub menu
15478      */
15479     /**
15480      * @cfg {String} text
15481      * The text to show on the menu item.
15482      */
15483     text: '',
15484      /**
15485      * @cfg {String} HTML to render in menu
15486      * The text to show on the menu item (HTML version).
15487      */
15488     html: '',
15489     /**
15490      * @cfg {String} icon
15491      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15492      */
15493     icon: undefined,
15494     /**
15495      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15496      */
15497     itemCls : "x-menu-item",
15498     /**
15499      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15500      */
15501     canActivate : true,
15502     /**
15503      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15504      */
15505     showDelay: 200,
15506     // doc'd in BaseItem
15507     hideDelay: 200,
15508
15509     // private
15510     ctype: "Roo.menu.Item",
15511     
15512     // private
15513     onRender : function(container, position){
15514         var el = document.createElement("a");
15515         el.hideFocus = true;
15516         el.unselectable = "on";
15517         el.href = this.href || "#";
15518         if(this.hrefTarget){
15519             el.target = this.hrefTarget;
15520         }
15521         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15522         
15523         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15524         
15525         el.innerHTML = String.format(
15526                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15527                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15528         this.el = el;
15529         Roo.menu.Item.superclass.onRender.call(this, container, position);
15530     },
15531
15532     /**
15533      * Sets the text to display in this menu item
15534      * @param {String} text The text to display
15535      * @param {Boolean} isHTML true to indicate text is pure html.
15536      */
15537     setText : function(text, isHTML){
15538         if (isHTML) {
15539             this.html = text;
15540         } else {
15541             this.text = text;
15542             this.html = '';
15543         }
15544         if(this.rendered){
15545             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15546      
15547             this.el.update(String.format(
15548                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15549                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15550             this.parentMenu.autoWidth();
15551         }
15552     },
15553
15554     // private
15555     handleClick : function(e){
15556         if(!this.href){ // if no link defined, stop the event automatically
15557             e.stopEvent();
15558         }
15559         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15560     },
15561
15562     // private
15563     activate : function(autoExpand){
15564         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15565             this.focus();
15566             if(autoExpand){
15567                 this.expandMenu();
15568             }
15569         }
15570         return true;
15571     },
15572
15573     // private
15574     shouldDeactivate : function(e){
15575         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15576             if(this.menu && this.menu.isVisible()){
15577                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15578             }
15579             return true;
15580         }
15581         return false;
15582     },
15583
15584     // private
15585     deactivate : function(){
15586         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15587         this.hideMenu();
15588     },
15589
15590     // private
15591     expandMenu : function(autoActivate){
15592         if(!this.disabled && this.menu){
15593             clearTimeout(this.hideTimer);
15594             delete this.hideTimer;
15595             if(!this.menu.isVisible() && !this.showTimer){
15596                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15597             }else if (this.menu.isVisible() && autoActivate){
15598                 this.menu.tryActivate(0, 1);
15599             }
15600         }
15601     },
15602
15603     // private
15604     deferExpand : function(autoActivate){
15605         delete this.showTimer;
15606         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15607         if(autoActivate){
15608             this.menu.tryActivate(0, 1);
15609         }
15610     },
15611
15612     // private
15613     hideMenu : function(){
15614         clearTimeout(this.showTimer);
15615         delete this.showTimer;
15616         if(!this.hideTimer && this.menu && this.menu.isVisible()){
15617             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15618         }
15619     },
15620
15621     // private
15622     deferHide : function(){
15623         delete this.hideTimer;
15624         this.menu.hide();
15625     }
15626 });/*
15627  * Based on:
15628  * Ext JS Library 1.1.1
15629  * Copyright(c) 2006-2007, Ext JS, LLC.
15630  *
15631  * Originally Released Under LGPL - original licence link has changed is not relivant.
15632  *
15633  * Fork - LGPL
15634  * <script type="text/javascript">
15635  */
15636  
15637 /**
15638  * @class Roo.menu.CheckItem
15639  * @extends Roo.menu.Item
15640  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
15641  * @constructor
15642  * Creates a new CheckItem
15643  * @param {Object} config Configuration options
15644  */
15645 Roo.menu.CheckItem = function(config){
15646     Roo.menu.CheckItem.superclass.constructor.call(this, config);
15647     this.addEvents({
15648         /**
15649          * @event beforecheckchange
15650          * Fires before the checked value is set, providing an opportunity to cancel if needed
15651          * @param {Roo.menu.CheckItem} this
15652          * @param {Boolean} checked The new checked value that will be set
15653          */
15654         "beforecheckchange" : true,
15655         /**
15656          * @event checkchange
15657          * Fires after the checked value has been set
15658          * @param {Roo.menu.CheckItem} this
15659          * @param {Boolean} checked The checked value that was set
15660          */
15661         "checkchange" : true
15662     });
15663     if(this.checkHandler){
15664         this.on('checkchange', this.checkHandler, this.scope);
15665     }
15666 };
15667 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
15668     /**
15669      * @cfg {String} group
15670      * All check items with the same group name will automatically be grouped into a single-select
15671      * radio button group (defaults to '')
15672      */
15673     /**
15674      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
15675      */
15676     itemCls : "x-menu-item x-menu-check-item",
15677     /**
15678      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
15679      */
15680     groupClass : "x-menu-group-item",
15681
15682     /**
15683      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
15684      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
15685      * initialized with checked = true will be rendered as checked.
15686      */
15687     checked: false,
15688
15689     // private
15690     ctype: "Roo.menu.CheckItem",
15691
15692     // private
15693     onRender : function(c){
15694         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
15695         if(this.group){
15696             this.el.addClass(this.groupClass);
15697         }
15698         Roo.menu.MenuMgr.registerCheckable(this);
15699         if(this.checked){
15700             this.checked = false;
15701             this.setChecked(true, true);
15702         }
15703     },
15704
15705     // private
15706     destroy : function(){
15707         if(this.rendered){
15708             Roo.menu.MenuMgr.unregisterCheckable(this);
15709         }
15710         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
15711     },
15712
15713     /**
15714      * Set the checked state of this item
15715      * @param {Boolean} checked The new checked value
15716      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
15717      */
15718     setChecked : function(state, suppressEvent){
15719         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
15720             if(this.container){
15721                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
15722             }
15723             this.checked = state;
15724             if(suppressEvent !== true){
15725                 this.fireEvent("checkchange", this, state);
15726             }
15727         }
15728     },
15729
15730     // private
15731     handleClick : function(e){
15732        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
15733            this.setChecked(!this.checked);
15734        }
15735        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
15736     }
15737 });/*
15738  * Based on:
15739  * Ext JS Library 1.1.1
15740  * Copyright(c) 2006-2007, Ext JS, LLC.
15741  *
15742  * Originally Released Under LGPL - original licence link has changed is not relivant.
15743  *
15744  * Fork - LGPL
15745  * <script type="text/javascript">
15746  */
15747  
15748 /**
15749  * @class Roo.menu.DateItem
15750  * @extends Roo.menu.Adapter
15751  * A menu item that wraps the {@link Roo.DatPicker} component.
15752  * @constructor
15753  * Creates a new DateItem
15754  * @param {Object} config Configuration options
15755  */
15756 Roo.menu.DateItem = function(config){
15757     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
15758     /** The Roo.DatePicker object @type Roo.DatePicker */
15759     this.picker = this.component;
15760     this.addEvents({select: true});
15761     
15762     this.picker.on("render", function(picker){
15763         picker.getEl().swallowEvent("click");
15764         picker.container.addClass("x-menu-date-item");
15765     });
15766
15767     this.picker.on("select", this.onSelect, this);
15768 };
15769
15770 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
15771     // private
15772     onSelect : function(picker, date){
15773         this.fireEvent("select", this, date, picker);
15774         Roo.menu.DateItem.superclass.handleClick.call(this);
15775     }
15776 });/*
15777  * Based on:
15778  * Ext JS Library 1.1.1
15779  * Copyright(c) 2006-2007, Ext JS, LLC.
15780  *
15781  * Originally Released Under LGPL - original licence link has changed is not relivant.
15782  *
15783  * Fork - LGPL
15784  * <script type="text/javascript">
15785  */
15786  
15787 /**
15788  * @class Roo.menu.ColorItem
15789  * @extends Roo.menu.Adapter
15790  * A menu item that wraps the {@link Roo.ColorPalette} component.
15791  * @constructor
15792  * Creates a new ColorItem
15793  * @param {Object} config Configuration options
15794  */
15795 Roo.menu.ColorItem = function(config){
15796     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
15797     /** The Roo.ColorPalette object @type Roo.ColorPalette */
15798     this.palette = this.component;
15799     this.relayEvents(this.palette, ["select"]);
15800     if(this.selectHandler){
15801         this.on('select', this.selectHandler, this.scope);
15802     }
15803 };
15804 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
15805  * Based on:
15806  * Ext JS Library 1.1.1
15807  * Copyright(c) 2006-2007, Ext JS, LLC.
15808  *
15809  * Originally Released Under LGPL - original licence link has changed is not relivant.
15810  *
15811  * Fork - LGPL
15812  * <script type="text/javascript">
15813  */
15814  
15815
15816 /**
15817  * @class Roo.menu.DateMenu
15818  * @extends Roo.menu.Menu
15819  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
15820  * @constructor
15821  * Creates a new DateMenu
15822  * @param {Object} config Configuration options
15823  */
15824 Roo.menu.DateMenu = function(config){
15825     Roo.menu.DateMenu.superclass.constructor.call(this, config);
15826     this.plain = true;
15827     var di = new Roo.menu.DateItem(config);
15828     this.add(di);
15829     /**
15830      * The {@link Roo.DatePicker} instance for this DateMenu
15831      * @type DatePicker
15832      */
15833     this.picker = di.picker;
15834     /**
15835      * @event select
15836      * @param {DatePicker} picker
15837      * @param {Date} date
15838      */
15839     this.relayEvents(di, ["select"]);
15840     this.on('beforeshow', function(){
15841         if(this.picker){
15842             this.picker.hideMonthPicker(false);
15843         }
15844     }, this);
15845 };
15846 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
15847     cls:'x-date-menu'
15848 });/*
15849  * Based on:
15850  * Ext JS Library 1.1.1
15851  * Copyright(c) 2006-2007, Ext JS, LLC.
15852  *
15853  * Originally Released Under LGPL - original licence link has changed is not relivant.
15854  *
15855  * Fork - LGPL
15856  * <script type="text/javascript">
15857  */
15858  
15859
15860 /**
15861  * @class Roo.menu.ColorMenu
15862  * @extends Roo.menu.Menu
15863  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
15864  * @constructor
15865  * Creates a new ColorMenu
15866  * @param {Object} config Configuration options
15867  */
15868 Roo.menu.ColorMenu = function(config){
15869     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
15870     this.plain = true;
15871     var ci = new Roo.menu.ColorItem(config);
15872     this.add(ci);
15873     /**
15874      * The {@link Roo.ColorPalette} instance for this ColorMenu
15875      * @type ColorPalette
15876      */
15877     this.palette = ci.palette;
15878     /**
15879      * @event select
15880      * @param {ColorPalette} palette
15881      * @param {String} color
15882      */
15883     this.relayEvents(ci, ["select"]);
15884 };
15885 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
15886  * Based on:
15887  * Ext JS Library 1.1.1
15888  * Copyright(c) 2006-2007, Ext JS, LLC.
15889  *
15890  * Originally Released Under LGPL - original licence link has changed is not relivant.
15891  *
15892  * Fork - LGPL
15893  * <script type="text/javascript">
15894  */
15895  
15896 /**
15897  * @class Roo.form.TextItem
15898  * @extends Roo.BoxComponent
15899  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15900  * @constructor
15901  * Creates a new TextItem
15902  * @param {Object} config Configuration options
15903  */
15904 Roo.form.TextItem = function(config){
15905     Roo.form.TextItem.superclass.constructor.call(this, config);
15906 };
15907
15908 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
15909     
15910     /**
15911      * @cfg {String} tag the tag for this item (default div)
15912      */
15913     tag : 'div',
15914     /**
15915      * @cfg {String} html the content for this item
15916      */
15917     html : '',
15918     
15919     getAutoCreate : function()
15920     {
15921         var cfg = {
15922             id: this.id,
15923             tag: this.tag,
15924             html: this.html,
15925             cls: 'x-form-item'
15926         };
15927         
15928         return cfg;
15929         
15930     },
15931     
15932     onRender : function(ct, position)
15933     {
15934         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
15935         
15936         if(!this.el){
15937             var cfg = this.getAutoCreate();
15938             if(!cfg.name){
15939                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
15940             }
15941             if (!cfg.name.length) {
15942                 delete cfg.name;
15943             }
15944             this.el = ct.createChild(cfg, position);
15945         }
15946     },
15947     /*
15948      * setHTML
15949      * @param {String} html update the Contents of the element.
15950      */
15951     setHTML : function(html)
15952     {
15953         this.fieldEl.dom.innerHTML = html;
15954     }
15955     
15956 });/*
15957  * Based on:
15958  * Ext JS Library 1.1.1
15959  * Copyright(c) 2006-2007, Ext JS, LLC.
15960  *
15961  * Originally Released Under LGPL - original licence link has changed is not relivant.
15962  *
15963  * Fork - LGPL
15964  * <script type="text/javascript">
15965  */
15966  
15967 /**
15968  * @class Roo.form.Field
15969  * @extends Roo.BoxComponent
15970  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15971  * @constructor
15972  * Creates a new Field
15973  * @param {Object} config Configuration options
15974  */
15975 Roo.form.Field = function(config){
15976     Roo.form.Field.superclass.constructor.call(this, config);
15977 };
15978
15979 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
15980     /**
15981      * @cfg {String} fieldLabel Label to use when rendering a form.
15982      */
15983        /**
15984      * @cfg {String} qtip Mouse over tip
15985      */
15986      
15987     /**
15988      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
15989      */
15990     invalidClass : "x-form-invalid",
15991     /**
15992      * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided (defaults to "The value in this field is invalid")
15993      */
15994     invalidText : "The value in this field is invalid",
15995     /**
15996      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
15997      */
15998     focusClass : "x-form-focus",
15999     /**
16000      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16001       automatic validation (defaults to "keyup").
16002      */
16003     validationEvent : "keyup",
16004     /**
16005      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16006      */
16007     validateOnBlur : true,
16008     /**
16009      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16010      */
16011     validationDelay : 250,
16012     /**
16013      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16014      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16015      */
16016     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16017     /**
16018      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16019      */
16020     fieldClass : "x-form-field",
16021     /**
16022      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16023      *<pre>
16024 Value         Description
16025 -----------   ----------------------------------------------------------------------
16026 qtip          Display a quick tip when the user hovers over the field
16027 title         Display a default browser title attribute popup
16028 under         Add a block div beneath the field containing the error text
16029 side          Add an error icon to the right of the field with a popup on hover
16030 [element id]  Add the error text directly to the innerHTML of the specified element
16031 </pre>
16032      */
16033     msgTarget : 'qtip',
16034     /**
16035      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16036      */
16037     msgFx : 'normal',
16038
16039     /**
16040      * @cfg {Boolean} readOnly True to mark the field as readOnly in HTML (defaults to false) -- Note: this only sets the element's readOnly DOM attribute.
16041      */
16042     readOnly : false,
16043
16044     /**
16045      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16046      */
16047     disabled : false,
16048
16049     /**
16050      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16051      */
16052     inputType : undefined,
16053     
16054     /**
16055      * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, not those which are built via applyTo (defaults to undefined).
16056          */
16057         tabIndex : undefined,
16058         
16059     // private
16060     isFormField : true,
16061
16062     // private
16063     hasFocus : false,
16064     /**
16065      * @property {Roo.Element} fieldEl
16066      * Element Containing the rendered Field (with label etc.)
16067      */
16068     /**
16069      * @cfg {Mixed} value A value to initialize this field with.
16070      */
16071     value : undefined,
16072
16073     /**
16074      * @cfg {String} name The field's HTML name attribute.
16075      */
16076     /**
16077      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16078      */
16079     // private
16080     loadedValue : false,
16081      
16082      
16083         // private ??
16084         initComponent : function(){
16085         Roo.form.Field.superclass.initComponent.call(this);
16086         this.addEvents({
16087             /**
16088              * @event focus
16089              * Fires when this field receives input focus.
16090              * @param {Roo.form.Field} this
16091              */
16092             focus : true,
16093             /**
16094              * @event blur
16095              * Fires when this field loses input focus.
16096              * @param {Roo.form.Field} this
16097              */
16098             blur : true,
16099             /**
16100              * @event specialkey
16101              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16102              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16103              * @param {Roo.form.Field} this
16104              * @param {Roo.EventObject} e The event object
16105              */
16106             specialkey : true,
16107             /**
16108              * @event change
16109              * Fires just before the field blurs if the field value has changed.
16110              * @param {Roo.form.Field} this
16111              * @param {Mixed} newValue The new value
16112              * @param {Mixed} oldValue The original value
16113              */
16114             change : true,
16115             /**
16116              * @event invalid
16117              * Fires after the field has been marked as invalid.
16118              * @param {Roo.form.Field} this
16119              * @param {String} msg The validation message
16120              */
16121             invalid : true,
16122             /**
16123              * @event valid
16124              * Fires after the field has been validated with no errors.
16125              * @param {Roo.form.Field} this
16126              */
16127             valid : true,
16128              /**
16129              * @event keyup
16130              * Fires after the key up
16131              * @param {Roo.form.Field} this
16132              * @param {Roo.EventObject}  e The event Object
16133              */
16134             keyup : true
16135         });
16136     },
16137
16138     /**
16139      * Returns the name attribute of the field if available
16140      * @return {String} name The field name
16141      */
16142     getName: function(){
16143          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16144     },
16145
16146     // private
16147     onRender : function(ct, position){
16148         Roo.form.Field.superclass.onRender.call(this, ct, position);
16149         if(!this.el){
16150             var cfg = this.getAutoCreate();
16151             if(!cfg.name){
16152                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16153             }
16154             if (!cfg.name.length) {
16155                 delete cfg.name;
16156             }
16157             if(this.inputType){
16158                 cfg.type = this.inputType;
16159             }
16160             this.el = ct.createChild(cfg, position);
16161         }
16162         var type = this.el.dom.type;
16163         if(type){
16164             if(type == 'password'){
16165                 type = 'text';
16166             }
16167             this.el.addClass('x-form-'+type);
16168         }
16169         if(this.readOnly){
16170             this.el.dom.readOnly = true;
16171         }
16172         if(this.tabIndex !== undefined){
16173             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16174         }
16175
16176         this.el.addClass([this.fieldClass, this.cls]);
16177         this.initValue();
16178     },
16179
16180     /**
16181      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16182      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16183      * @return {Roo.form.Field} this
16184      */
16185     applyTo : function(target){
16186         this.allowDomMove = false;
16187         this.el = Roo.get(target);
16188         this.render(this.el.dom.parentNode);
16189         return this;
16190     },
16191
16192     // private
16193     initValue : function(){
16194         if(this.value !== undefined){
16195             this.setValue(this.value);
16196         }else if(this.el.dom.value.length > 0){
16197             this.setValue(this.el.dom.value);
16198         }
16199     },
16200
16201     /**
16202      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16203      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16204      */
16205     isDirty : function() {
16206         if(this.disabled) {
16207             return false;
16208         }
16209         return String(this.getValue()) !== String(this.originalValue);
16210     },
16211
16212     /**
16213      * stores the current value in loadedValue
16214      */
16215     resetHasChanged : function()
16216     {
16217         this.loadedValue = String(this.getValue());
16218     },
16219     /**
16220      * checks the current value against the 'loaded' value.
16221      * Note - will return false if 'resetHasChanged' has not been called first.
16222      */
16223     hasChanged : function()
16224     {
16225         if(this.disabled || this.readOnly) {
16226             return false;
16227         }
16228         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16229     },
16230     
16231     
16232     
16233     // private
16234     afterRender : function(){
16235         Roo.form.Field.superclass.afterRender.call(this);
16236         this.initEvents();
16237     },
16238
16239     // private
16240     fireKey : function(e){
16241         //Roo.log('field ' + e.getKey());
16242         if(e.isNavKeyPress()){
16243             this.fireEvent("specialkey", this, e);
16244         }
16245     },
16246
16247     /**
16248      * Resets the current field value to the originally loaded value and clears any validation messages
16249      */
16250     reset : function(){
16251         this.setValue(this.resetValue);
16252         this.originalValue = this.getValue();
16253         this.clearInvalid();
16254     },
16255
16256     // private
16257     initEvents : function(){
16258         // safari killled keypress - so keydown is now used..
16259         this.el.on("keydown" , this.fireKey,  this);
16260         this.el.on("focus", this.onFocus,  this);
16261         this.el.on("blur", this.onBlur,  this);
16262         this.el.relayEvent('keyup', this);
16263
16264         // reference to original value for reset
16265         this.originalValue = this.getValue();
16266         this.resetValue =  this.getValue();
16267     },
16268
16269     // private
16270     onFocus : function(){
16271         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16272             this.el.addClass(this.focusClass);
16273         }
16274         if(!this.hasFocus){
16275             this.hasFocus = true;
16276             this.startValue = this.getValue();
16277             this.fireEvent("focus", this);
16278         }
16279     },
16280
16281     beforeBlur : Roo.emptyFn,
16282
16283     // private
16284     onBlur : function(){
16285         this.beforeBlur();
16286         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16287             this.el.removeClass(this.focusClass);
16288         }
16289         this.hasFocus = false;
16290         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16291             this.validate();
16292         }
16293         var v = this.getValue();
16294         if(String(v) !== String(this.startValue)){
16295             this.fireEvent('change', this, v, this.startValue);
16296         }
16297         this.fireEvent("blur", this);
16298     },
16299
16300     /**
16301      * Returns whether or not the field value is currently valid
16302      * @param {Boolean} preventMark True to disable marking the field invalid
16303      * @return {Boolean} True if the value is valid, else false
16304      */
16305     isValid : function(preventMark){
16306         if(this.disabled){
16307             return true;
16308         }
16309         var restore = this.preventMark;
16310         this.preventMark = preventMark === true;
16311         var v = this.validateValue(this.processValue(this.getRawValue()));
16312         this.preventMark = restore;
16313         return v;
16314     },
16315
16316     /**
16317      * Validates the field value
16318      * @return {Boolean} True if the value is valid, else false
16319      */
16320     validate : function(){
16321         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16322             this.clearInvalid();
16323             return true;
16324         }
16325         return false;
16326     },
16327
16328     processValue : function(value){
16329         return value;
16330     },
16331
16332     // private
16333     // Subclasses should provide the validation implementation by overriding this
16334     validateValue : function(value){
16335         return true;
16336     },
16337
16338     /**
16339      * Mark this field as invalid
16340      * @param {String} msg The validation message
16341      */
16342     markInvalid : function(msg){
16343         if(!this.rendered || this.preventMark){ // not rendered
16344             return;
16345         }
16346         
16347         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16348         
16349         obj.el.addClass(this.invalidClass);
16350         msg = msg || this.invalidText;
16351         switch(this.msgTarget){
16352             case 'qtip':
16353                 obj.el.dom.qtip = msg;
16354                 obj.el.dom.qclass = 'x-form-invalid-tip';
16355                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16356                     Roo.QuickTips.enable();
16357                 }
16358                 break;
16359             case 'title':
16360                 this.el.dom.title = msg;
16361                 break;
16362             case 'under':
16363                 if(!this.errorEl){
16364                     var elp = this.el.findParent('.x-form-element', 5, true);
16365                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16366                     this.errorEl.setWidth(elp.getWidth(true)-20);
16367                 }
16368                 this.errorEl.update(msg);
16369                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16370                 break;
16371             case 'side':
16372                 if(!this.errorIcon){
16373                     var elp = this.el.findParent('.x-form-element', 5, true);
16374                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16375                 }
16376                 this.alignErrorIcon();
16377                 this.errorIcon.dom.qtip = msg;
16378                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16379                 this.errorIcon.show();
16380                 this.on('resize', this.alignErrorIcon, this);
16381                 break;
16382             default:
16383                 var t = Roo.getDom(this.msgTarget);
16384                 t.innerHTML = msg;
16385                 t.style.display = this.msgDisplay;
16386                 break;
16387         }
16388         this.fireEvent('invalid', this, msg);
16389     },
16390
16391     // private
16392     alignErrorIcon : function(){
16393         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16394     },
16395
16396     /**
16397      * Clear any invalid styles/messages for this field
16398      */
16399     clearInvalid : function(){
16400         if(!this.rendered || this.preventMark){ // not rendered
16401             return;
16402         }
16403         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16404         
16405         obj.el.removeClass(this.invalidClass);
16406         switch(this.msgTarget){
16407             case 'qtip':
16408                 obj.el.dom.qtip = '';
16409                 break;
16410             case 'title':
16411                 this.el.dom.title = '';
16412                 break;
16413             case 'under':
16414                 if(this.errorEl){
16415                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16416                 }
16417                 break;
16418             case 'side':
16419                 if(this.errorIcon){
16420                     this.errorIcon.dom.qtip = '';
16421                     this.errorIcon.hide();
16422                     this.un('resize', this.alignErrorIcon, this);
16423                 }
16424                 break;
16425             default:
16426                 var t = Roo.getDom(this.msgTarget);
16427                 t.innerHTML = '';
16428                 t.style.display = 'none';
16429                 break;
16430         }
16431         this.fireEvent('valid', this);
16432     },
16433
16434     /**
16435      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16436      * @return {Mixed} value The field value
16437      */
16438     getRawValue : function(){
16439         var v = this.el.getValue();
16440         
16441         return v;
16442     },
16443
16444     /**
16445      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16446      * @return {Mixed} value The field value
16447      */
16448     getValue : function(){
16449         var v = this.el.getValue();
16450          
16451         return v;
16452     },
16453
16454     /**
16455      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16456      * @param {Mixed} value The value to set
16457      */
16458     setRawValue : function(v){
16459         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16460     },
16461
16462     /**
16463      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16464      * @param {Mixed} value The value to set
16465      */
16466     setValue : function(v){
16467         this.value = v;
16468         if(this.rendered){
16469             this.el.dom.value = (v === null || v === undefined ? '' : v);
16470              this.validate();
16471         }
16472     },
16473
16474     adjustSize : function(w, h){
16475         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16476         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16477         return s;
16478     },
16479
16480     adjustWidth : function(tag, w){
16481         tag = tag.toLowerCase();
16482         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16483             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16484                 if(tag == 'input'){
16485                     return w + 2;
16486                 }
16487                 if(tag == 'textarea'){
16488                     return w-2;
16489                 }
16490             }else if(Roo.isOpera){
16491                 if(tag == 'input'){
16492                     return w + 2;
16493                 }
16494                 if(tag == 'textarea'){
16495                     return w-2;
16496                 }
16497             }
16498         }
16499         return w;
16500     }
16501 });
16502
16503
16504 // anything other than normal should be considered experimental
16505 Roo.form.Field.msgFx = {
16506     normal : {
16507         show: function(msgEl, f){
16508             msgEl.setDisplayed('block');
16509         },
16510
16511         hide : function(msgEl, f){
16512             msgEl.setDisplayed(false).update('');
16513         }
16514     },
16515
16516     slide : {
16517         show: function(msgEl, f){
16518             msgEl.slideIn('t', {stopFx:true});
16519         },
16520
16521         hide : function(msgEl, f){
16522             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16523         }
16524     },
16525
16526     slideRight : {
16527         show: function(msgEl, f){
16528             msgEl.fixDisplay();
16529             msgEl.alignTo(f.el, 'tl-tr');
16530             msgEl.slideIn('l', {stopFx:true});
16531         },
16532
16533         hide : function(msgEl, f){
16534             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16535         }
16536     }
16537 };/*
16538  * Based on:
16539  * Ext JS Library 1.1.1
16540  * Copyright(c) 2006-2007, Ext JS, LLC.
16541  *
16542  * Originally Released Under LGPL - original licence link has changed is not relivant.
16543  *
16544  * Fork - LGPL
16545  * <script type="text/javascript">
16546  */
16547  
16548
16549 /**
16550  * @class Roo.form.TextField
16551  * @extends Roo.form.Field
16552  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16553  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16554  * @constructor
16555  * Creates a new TextField
16556  * @param {Object} config Configuration options
16557  */
16558 Roo.form.TextField = function(config){
16559     Roo.form.TextField.superclass.constructor.call(this, config);
16560     this.addEvents({
16561         /**
16562          * @event autosize
16563          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16564          * according to the default logic, but this event provides a hook for the developer to apply additional
16565          * logic at runtime to resize the field if needed.
16566              * @param {Roo.form.Field} this This text field
16567              * @param {Number} width The new field width
16568              */
16569         autosize : true
16570     });
16571 };
16572
16573 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16574     /**
16575      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16576      */
16577     grow : false,
16578     /**
16579      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16580      */
16581     growMin : 30,
16582     /**
16583      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16584      */
16585     growMax : 800,
16586     /**
16587      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16588      */
16589     vtype : null,
16590     /**
16591      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16592      */
16593     maskRe : null,
16594     /**
16595      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16596      */
16597     disableKeyFilter : false,
16598     /**
16599      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16600      */
16601     allowBlank : true,
16602     /**
16603      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16604      */
16605     minLength : 0,
16606     /**
16607      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16608      */
16609     maxLength : Number.MAX_VALUE,
16610     /**
16611      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16612      */
16613     minLengthText : "The minimum length for this field is {0}",
16614     /**
16615      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16616      */
16617     maxLengthText : "The maximum length for this field is {0}",
16618     /**
16619      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16620      */
16621     selectOnFocus : false,
16622     /**
16623      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
16624      */    
16625     allowLeadingSpace : false,
16626     /**
16627      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16628      */
16629     blankText : "This field is required",
16630     /**
16631      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16632      * If available, this function will be called only after the basic validators all return true, and will be passed the
16633      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16634      */
16635     validator : null,
16636     /**
16637      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16638      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16639      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
16640      */
16641     regex : null,
16642     /**
16643      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16644      */
16645     regexText : "",
16646     /**
16647      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16648      */
16649     emptyText : null,
16650    
16651
16652     // private
16653     initEvents : function()
16654     {
16655         if (this.emptyText) {
16656             this.el.attr('placeholder', this.emptyText);
16657         }
16658         
16659         Roo.form.TextField.superclass.initEvents.call(this);
16660         if(this.validationEvent == 'keyup'){
16661             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16662             this.el.on('keyup', this.filterValidation, this);
16663         }
16664         else if(this.validationEvent !== false){
16665             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16666         }
16667         
16668         if(this.selectOnFocus){
16669             this.on("focus", this.preFocus, this);
16670         }
16671         if (!this.allowLeadingSpace) {
16672             this.on('blur', this.cleanLeadingSpace, this);
16673         }
16674         
16675         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16676             this.el.on("keypress", this.filterKeys, this);
16677         }
16678         if(this.grow){
16679             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
16680             this.el.on("click", this.autoSize,  this);
16681         }
16682         if(this.el.is('input[type=password]') && Roo.isSafari){
16683             this.el.on('keydown', this.SafariOnKeyDown, this);
16684         }
16685     },
16686
16687     processValue : function(value){
16688         if(this.stripCharsRe){
16689             var newValue = value.replace(this.stripCharsRe, '');
16690             if(newValue !== value){
16691                 this.setRawValue(newValue);
16692                 return newValue;
16693             }
16694         }
16695         return value;
16696     },
16697
16698     filterValidation : function(e){
16699         if(!e.isNavKeyPress()){
16700             this.validationTask.delay(this.validationDelay);
16701         }
16702     },
16703
16704     // private
16705     onKeyUp : function(e){
16706         if(!e.isNavKeyPress()){
16707             this.autoSize();
16708         }
16709     },
16710     // private - clean the leading white space
16711     cleanLeadingSpace : function(e)
16712     {
16713         if ( this.inputType == 'file') {
16714             return;
16715         }
16716         
16717         this.setValue((this.getValue() + '').replace(/^\s+/,''));
16718     },
16719     /**
16720      * Resets the current field value to the originally-loaded value and clears any validation messages.
16721      *  
16722      */
16723     reset : function(){
16724         Roo.form.TextField.superclass.reset.call(this);
16725        
16726     }, 
16727     // private
16728     preFocus : function(){
16729         
16730         if(this.selectOnFocus){
16731             this.el.dom.select();
16732         }
16733     },
16734
16735     
16736     // private
16737     filterKeys : function(e){
16738         var k = e.getKey();
16739         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
16740             return;
16741         }
16742         var c = e.getCharCode(), cc = String.fromCharCode(c);
16743         if(Roo.isIE && (e.isSpecialKey() || !cc)){
16744             return;
16745         }
16746         if(!this.maskRe.test(cc)){
16747             e.stopEvent();
16748         }
16749     },
16750
16751     setValue : function(v){
16752         
16753         Roo.form.TextField.superclass.setValue.apply(this, arguments);
16754         
16755         this.autoSize();
16756     },
16757
16758     /**
16759      * Validates a value according to the field's validation rules and marks the field as invalid
16760      * if the validation fails
16761      * @param {Mixed} value The value to validate
16762      * @return {Boolean} True if the value is valid, else false
16763      */
16764     validateValue : function(value){
16765         if(value.length < 1)  { // if it's blank
16766              if(this.allowBlank){
16767                 this.clearInvalid();
16768                 return true;
16769              }else{
16770                 this.markInvalid(this.blankText);
16771                 return false;
16772              }
16773         }
16774         if(value.length < this.minLength){
16775             this.markInvalid(String.format(this.minLengthText, this.minLength));
16776             return false;
16777         }
16778         if(value.length > this.maxLength){
16779             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
16780             return false;
16781         }
16782         if(this.vtype){
16783             var vt = Roo.form.VTypes;
16784             if(!vt[this.vtype](value, this)){
16785                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
16786                 return false;
16787             }
16788         }
16789         if(typeof this.validator == "function"){
16790             var msg = this.validator(value);
16791             if(msg !== true){
16792                 this.markInvalid(msg);
16793                 return false;
16794             }
16795         }
16796         if(this.regex && !this.regex.test(value)){
16797             this.markInvalid(this.regexText);
16798             return false;
16799         }
16800         return true;
16801     },
16802
16803     /**
16804      * Selects text in this field
16805      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
16806      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
16807      */
16808     selectText : function(start, end){
16809         var v = this.getRawValue();
16810         if(v.length > 0){
16811             start = start === undefined ? 0 : start;
16812             end = end === undefined ? v.length : end;
16813             var d = this.el.dom;
16814             if(d.setSelectionRange){
16815                 d.setSelectionRange(start, end);
16816             }else if(d.createTextRange){
16817                 var range = d.createTextRange();
16818                 range.moveStart("character", start);
16819                 range.moveEnd("character", v.length-end);
16820                 range.select();
16821             }
16822         }
16823     },
16824
16825     /**
16826      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
16827      * This only takes effect if grow = true, and fires the autosize event.
16828      */
16829     autoSize : function(){
16830         if(!this.grow || !this.rendered){
16831             return;
16832         }
16833         if(!this.metrics){
16834             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
16835         }
16836         var el = this.el;
16837         var v = el.dom.value;
16838         var d = document.createElement('div');
16839         d.appendChild(document.createTextNode(v));
16840         v = d.innerHTML;
16841         d = null;
16842         v += "&#160;";
16843         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
16844         this.el.setWidth(w);
16845         this.fireEvent("autosize", this, w);
16846     },
16847     
16848     // private
16849     SafariOnKeyDown : function(event)
16850     {
16851         // this is a workaround for a password hang bug on chrome/ webkit.
16852         
16853         var isSelectAll = false;
16854         
16855         if(this.el.dom.selectionEnd > 0){
16856             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
16857         }
16858         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
16859             event.preventDefault();
16860             this.setValue('');
16861             return;
16862         }
16863         
16864         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
16865             
16866             event.preventDefault();
16867             // this is very hacky as keydown always get's upper case.
16868             
16869             var cc = String.fromCharCode(event.getCharCode());
16870             
16871             
16872             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
16873             
16874         }
16875         
16876         
16877     }
16878 });/*
16879  * Based on:
16880  * Ext JS Library 1.1.1
16881  * Copyright(c) 2006-2007, Ext JS, LLC.
16882  *
16883  * Originally Released Under LGPL - original licence link has changed is not relivant.
16884  *
16885  * Fork - LGPL
16886  * <script type="text/javascript">
16887  */
16888  
16889 /**
16890  * @class Roo.form.Hidden
16891  * @extends Roo.form.TextField
16892  * Simple Hidden element used on forms 
16893  * 
16894  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
16895  * 
16896  * @constructor
16897  * Creates a new Hidden form element.
16898  * @param {Object} config Configuration options
16899  */
16900
16901
16902
16903 // easy hidden field...
16904 Roo.form.Hidden = function(config){
16905     Roo.form.Hidden.superclass.constructor.call(this, config);
16906 };
16907   
16908 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
16909     fieldLabel:      '',
16910     inputType:      'hidden',
16911     width:          50,
16912     allowBlank:     true,
16913     labelSeparator: '',
16914     hidden:         true,
16915     itemCls :       'x-form-item-display-none'
16916
16917
16918 });
16919
16920
16921 /*
16922  * Based on:
16923  * Ext JS Library 1.1.1
16924  * Copyright(c) 2006-2007, Ext JS, LLC.
16925  *
16926  * Originally Released Under LGPL - original licence link has changed is not relivant.
16927  *
16928  * Fork - LGPL
16929  * <script type="text/javascript">
16930  */
16931  
16932 /**
16933  * @class Roo.form.TriggerField
16934  * @extends Roo.form.TextField
16935  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
16936  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
16937  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
16938  * for which you can provide a custom implementation.  For example:
16939  * <pre><code>
16940 var trigger = new Roo.form.TriggerField();
16941 trigger.onTriggerClick = myTriggerFn;
16942 trigger.applyTo('my-field');
16943 </code></pre>
16944  *
16945  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
16946  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
16947  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
16948  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
16949  * @constructor
16950  * Create a new TriggerField.
16951  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
16952  * to the base TextField)
16953  */
16954 Roo.form.TriggerField = function(config){
16955     this.mimicing = false;
16956     Roo.form.TriggerField.superclass.constructor.call(this, config);
16957 };
16958
16959 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
16960     /**
16961      * @cfg {String} triggerClass A CSS class to apply to the trigger
16962      */
16963     /**
16964      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16965      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
16966      */
16967     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
16968     /**
16969      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
16970      */
16971     hideTrigger:false,
16972
16973     /** @cfg {Boolean} grow @hide */
16974     /** @cfg {Number} growMin @hide */
16975     /** @cfg {Number} growMax @hide */
16976
16977     /**
16978      * @hide 
16979      * @method
16980      */
16981     autoSize: Roo.emptyFn,
16982     // private
16983     monitorTab : true,
16984     // private
16985     deferHeight : true,
16986
16987     
16988     actionMode : 'wrap',
16989     // private
16990     onResize : function(w, h){
16991         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
16992         if(typeof w == 'number'){
16993             var x = w - this.trigger.getWidth();
16994             this.el.setWidth(this.adjustWidth('input', x));
16995             this.trigger.setStyle('left', x+'px');
16996         }
16997     },
16998
16999     // private
17000     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17001
17002     // private
17003     getResizeEl : function(){
17004         return this.wrap;
17005     },
17006
17007     // private
17008     getPositionEl : function(){
17009         return this.wrap;
17010     },
17011
17012     // private
17013     alignErrorIcon : function(){
17014         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17015     },
17016
17017     // private
17018     onRender : function(ct, position){
17019         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17020         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17021         this.trigger = this.wrap.createChild(this.triggerConfig ||
17022                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17023         if(this.hideTrigger){
17024             this.trigger.setDisplayed(false);
17025         }
17026         this.initTrigger();
17027         if(!this.width){
17028             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17029         }
17030     },
17031
17032     // private
17033     initTrigger : function(){
17034         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17035         this.trigger.addClassOnOver('x-form-trigger-over');
17036         this.trigger.addClassOnClick('x-form-trigger-click');
17037     },
17038
17039     // private
17040     onDestroy : function(){
17041         if(this.trigger){
17042             this.trigger.removeAllListeners();
17043             this.trigger.remove();
17044         }
17045         if(this.wrap){
17046             this.wrap.remove();
17047         }
17048         Roo.form.TriggerField.superclass.onDestroy.call(this);
17049     },
17050
17051     // private
17052     onFocus : function(){
17053         Roo.form.TriggerField.superclass.onFocus.call(this);
17054         if(!this.mimicing){
17055             this.wrap.addClass('x-trigger-wrap-focus');
17056             this.mimicing = true;
17057             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17058             if(this.monitorTab){
17059                 this.el.on("keydown", this.checkTab, this);
17060             }
17061         }
17062     },
17063
17064     // private
17065     checkTab : function(e){
17066         if(e.getKey() == e.TAB){
17067             this.triggerBlur();
17068         }
17069     },
17070
17071     // private
17072     onBlur : function(){
17073         // do nothing
17074     },
17075
17076     // private
17077     mimicBlur : function(e, t){
17078         if(!this.wrap.contains(t) && this.validateBlur()){
17079             this.triggerBlur();
17080         }
17081     },
17082
17083     // private
17084     triggerBlur : function(){
17085         this.mimicing = false;
17086         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17087         if(this.monitorTab){
17088             this.el.un("keydown", this.checkTab, this);
17089         }
17090         this.wrap.removeClass('x-trigger-wrap-focus');
17091         Roo.form.TriggerField.superclass.onBlur.call(this);
17092     },
17093
17094     // private
17095     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17096     validateBlur : function(e, t){
17097         return true;
17098     },
17099
17100     // private
17101     onDisable : function(){
17102         Roo.form.TriggerField.superclass.onDisable.call(this);
17103         if(this.wrap){
17104             this.wrap.addClass('x-item-disabled');
17105         }
17106     },
17107
17108     // private
17109     onEnable : function(){
17110         Roo.form.TriggerField.superclass.onEnable.call(this);
17111         if(this.wrap){
17112             this.wrap.removeClass('x-item-disabled');
17113         }
17114     },
17115
17116     // private
17117     onShow : function(){
17118         var ae = this.getActionEl();
17119         
17120         if(ae){
17121             ae.dom.style.display = '';
17122             ae.dom.style.visibility = 'visible';
17123         }
17124     },
17125
17126     // private
17127     
17128     onHide : function(){
17129         var ae = this.getActionEl();
17130         ae.dom.style.display = 'none';
17131     },
17132
17133     /**
17134      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17135      * by an implementing function.
17136      * @method
17137      * @param {EventObject} e
17138      */
17139     onTriggerClick : Roo.emptyFn
17140 });
17141
17142 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17143 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17144 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17145 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17146     initComponent : function(){
17147         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17148
17149         this.triggerConfig = {
17150             tag:'span', cls:'x-form-twin-triggers', cn:[
17151             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17152             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17153         ]};
17154     },
17155
17156     getTrigger : function(index){
17157         return this.triggers[index];
17158     },
17159
17160     initTrigger : function(){
17161         var ts = this.trigger.select('.x-form-trigger', true);
17162         this.wrap.setStyle('overflow', 'hidden');
17163         var triggerField = this;
17164         ts.each(function(t, all, index){
17165             t.hide = function(){
17166                 var w = triggerField.wrap.getWidth();
17167                 this.dom.style.display = 'none';
17168                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17169             };
17170             t.show = function(){
17171                 var w = triggerField.wrap.getWidth();
17172                 this.dom.style.display = '';
17173                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17174             };
17175             var triggerIndex = 'Trigger'+(index+1);
17176
17177             if(this['hide'+triggerIndex]){
17178                 t.dom.style.display = 'none';
17179             }
17180             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17181             t.addClassOnOver('x-form-trigger-over');
17182             t.addClassOnClick('x-form-trigger-click');
17183         }, this);
17184         this.triggers = ts.elements;
17185     },
17186
17187     onTrigger1Click : Roo.emptyFn,
17188     onTrigger2Click : Roo.emptyFn
17189 });/*
17190  * Based on:
17191  * Ext JS Library 1.1.1
17192  * Copyright(c) 2006-2007, Ext JS, LLC.
17193  *
17194  * Originally Released Under LGPL - original licence link has changed is not relivant.
17195  *
17196  * Fork - LGPL
17197  * <script type="text/javascript">
17198  */
17199  
17200 /**
17201  * @class Roo.form.TextArea
17202  * @extends Roo.form.TextField
17203  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17204  * support for auto-sizing.
17205  * @constructor
17206  * Creates a new TextArea
17207  * @param {Object} config Configuration options
17208  */
17209 Roo.form.TextArea = function(config){
17210     Roo.form.TextArea.superclass.constructor.call(this, config);
17211     // these are provided exchanges for backwards compat
17212     // minHeight/maxHeight were replaced by growMin/growMax to be
17213     // compatible with TextField growing config values
17214     if(this.minHeight !== undefined){
17215         this.growMin = this.minHeight;
17216     }
17217     if(this.maxHeight !== undefined){
17218         this.growMax = this.maxHeight;
17219     }
17220 };
17221
17222 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17223     /**
17224      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17225      */
17226     growMin : 60,
17227     /**
17228      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17229      */
17230     growMax: 1000,
17231     /**
17232      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17233      * in the field (equivalent to setting overflow: hidden, defaults to false)
17234      */
17235     preventScrollbars: false,
17236     /**
17237      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17238      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17239      */
17240
17241     // private
17242     onRender : function(ct, position){
17243         if(!this.el){
17244             this.defaultAutoCreate = {
17245                 tag: "textarea",
17246                 style:"width:300px;height:60px;",
17247                 autocomplete: "new-password"
17248             };
17249         }
17250         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17251         if(this.grow){
17252             this.textSizeEl = Roo.DomHelper.append(document.body, {
17253                 tag: "pre", cls: "x-form-grow-sizer"
17254             });
17255             if(this.preventScrollbars){
17256                 this.el.setStyle("overflow", "hidden");
17257             }
17258             this.el.setHeight(this.growMin);
17259         }
17260     },
17261
17262     onDestroy : function(){
17263         if(this.textSizeEl){
17264             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17265         }
17266         Roo.form.TextArea.superclass.onDestroy.call(this);
17267     },
17268
17269     // private
17270     onKeyUp : function(e){
17271         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17272             this.autoSize();
17273         }
17274     },
17275
17276     /**
17277      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17278      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17279      */
17280     autoSize : function(){
17281         if(!this.grow || !this.textSizeEl){
17282             return;
17283         }
17284         var el = this.el;
17285         var v = el.dom.value;
17286         var ts = this.textSizeEl;
17287
17288         ts.innerHTML = '';
17289         ts.appendChild(document.createTextNode(v));
17290         v = ts.innerHTML;
17291
17292         Roo.fly(ts).setWidth(this.el.getWidth());
17293         if(v.length < 1){
17294             v = "&#160;&#160;";
17295         }else{
17296             if(Roo.isIE){
17297                 v = v.replace(/\n/g, '<p>&#160;</p>');
17298             }
17299             v += "&#160;\n&#160;";
17300         }
17301         ts.innerHTML = v;
17302         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17303         if(h != this.lastHeight){
17304             this.lastHeight = h;
17305             this.el.setHeight(h);
17306             this.fireEvent("autosize", this, h);
17307         }
17308     }
17309 });/*
17310  * Based on:
17311  * Ext JS Library 1.1.1
17312  * Copyright(c) 2006-2007, Ext JS, LLC.
17313  *
17314  * Originally Released Under LGPL - original licence link has changed is not relivant.
17315  *
17316  * Fork - LGPL
17317  * <script type="text/javascript">
17318  */
17319  
17320
17321 /**
17322  * @class Roo.form.NumberField
17323  * @extends Roo.form.TextField
17324  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17325  * @constructor
17326  * Creates a new NumberField
17327  * @param {Object} config Configuration options
17328  */
17329 Roo.form.NumberField = function(config){
17330     Roo.form.NumberField.superclass.constructor.call(this, config);
17331 };
17332
17333 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17334     /**
17335      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17336      */
17337     fieldClass: "x-form-field x-form-num-field",
17338     /**
17339      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17340      */
17341     allowDecimals : true,
17342     /**
17343      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17344      */
17345     decimalSeparator : ".",
17346     /**
17347      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17348      */
17349     decimalPrecision : 2,
17350     /**
17351      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17352      */
17353     allowNegative : true,
17354     /**
17355      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17356      */
17357     minValue : Number.NEGATIVE_INFINITY,
17358     /**
17359      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17360      */
17361     maxValue : Number.MAX_VALUE,
17362     /**
17363      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17364      */
17365     minText : "The minimum value for this field is {0}",
17366     /**
17367      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17368      */
17369     maxText : "The maximum value for this field is {0}",
17370     /**
17371      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17372      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17373      */
17374     nanText : "{0} is not a valid number",
17375
17376     // private
17377     initEvents : function(){
17378         Roo.form.NumberField.superclass.initEvents.call(this);
17379         var allowed = "0123456789";
17380         if(this.allowDecimals){
17381             allowed += this.decimalSeparator;
17382         }
17383         if(this.allowNegative){
17384             allowed += "-";
17385         }
17386         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17387         var keyPress = function(e){
17388             var k = e.getKey();
17389             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17390                 return;
17391             }
17392             var c = e.getCharCode();
17393             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17394                 e.stopEvent();
17395             }
17396         };
17397         this.el.on("keypress", keyPress, this);
17398     },
17399
17400     // private
17401     validateValue : function(value){
17402         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17403             return false;
17404         }
17405         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17406              return true;
17407         }
17408         var num = this.parseValue(value);
17409         if(isNaN(num)){
17410             this.markInvalid(String.format(this.nanText, value));
17411             return false;
17412         }
17413         if(num < this.minValue){
17414             this.markInvalid(String.format(this.minText, this.minValue));
17415             return false;
17416         }
17417         if(num > this.maxValue){
17418             this.markInvalid(String.format(this.maxText, this.maxValue));
17419             return false;
17420         }
17421         return true;
17422     },
17423
17424     getValue : function(){
17425         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17426     },
17427
17428     // private
17429     parseValue : function(value){
17430         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17431         return isNaN(value) ? '' : value;
17432     },
17433
17434     // private
17435     fixPrecision : function(value){
17436         var nan = isNaN(value);
17437         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17438             return nan ? '' : value;
17439         }
17440         return parseFloat(value).toFixed(this.decimalPrecision);
17441     },
17442
17443     setValue : function(v){
17444         v = this.fixPrecision(v);
17445         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17446     },
17447
17448     // private
17449     decimalPrecisionFcn : function(v){
17450         return Math.floor(v);
17451     },
17452
17453     beforeBlur : function(){
17454         var v = this.parseValue(this.getRawValue());
17455         if(v){
17456             this.setValue(v);
17457         }
17458     }
17459 });/*
17460  * Based on:
17461  * Ext JS Library 1.1.1
17462  * Copyright(c) 2006-2007, Ext JS, LLC.
17463  *
17464  * Originally Released Under LGPL - original licence link has changed is not relivant.
17465  *
17466  * Fork - LGPL
17467  * <script type="text/javascript">
17468  */
17469  
17470 /**
17471  * @class Roo.form.DateField
17472  * @extends Roo.form.TriggerField
17473  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17474 * @constructor
17475 * Create a new DateField
17476 * @param {Object} config
17477  */
17478 Roo.form.DateField = function(config)
17479 {
17480     Roo.form.DateField.superclass.constructor.call(this, config);
17481     
17482       this.addEvents({
17483          
17484         /**
17485          * @event select
17486          * Fires when a date is selected
17487              * @param {Roo.form.DateField} combo This combo box
17488              * @param {Date} date The date selected
17489              */
17490         'select' : true
17491          
17492     });
17493     
17494     
17495     if(typeof this.minValue == "string") {
17496         this.minValue = this.parseDate(this.minValue);
17497     }
17498     if(typeof this.maxValue == "string") {
17499         this.maxValue = this.parseDate(this.maxValue);
17500     }
17501     this.ddMatch = null;
17502     if(this.disabledDates){
17503         var dd = this.disabledDates;
17504         var re = "(?:";
17505         for(var i = 0; i < dd.length; i++){
17506             re += dd[i];
17507             if(i != dd.length-1) {
17508                 re += "|";
17509             }
17510         }
17511         this.ddMatch = new RegExp(re + ")");
17512     }
17513 };
17514
17515 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17516     /**
17517      * @cfg {String} format
17518      * The default date format string which can be overriden for localization support.  The format must be
17519      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17520      */
17521     format : "m/d/y",
17522     /**
17523      * @cfg {String} altFormats
17524      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17525      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17526      */
17527     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17528     /**
17529      * @cfg {Array} disabledDays
17530      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17531      */
17532     disabledDays : null,
17533     /**
17534      * @cfg {String} disabledDaysText
17535      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17536      */
17537     disabledDaysText : "Disabled",
17538     /**
17539      * @cfg {Array} disabledDates
17540      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17541      * expression so they are very powerful. Some examples:
17542      * <ul>
17543      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17544      * <li>["03/08", "09/16"] would disable those days for every year</li>
17545      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17546      * <li>["03/../2006"] would disable every day in March 2006</li>
17547      * <li>["^03"] would disable every day in every March</li>
17548      * </ul>
17549      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17550      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17551      */
17552     disabledDates : null,
17553     /**
17554      * @cfg {String} disabledDatesText
17555      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17556      */
17557     disabledDatesText : "Disabled",
17558     /**
17559      * @cfg {Date/String} minValue
17560      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17561      * valid format (defaults to null).
17562      */
17563     minValue : null,
17564     /**
17565      * @cfg {Date/String} maxValue
17566      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17567      * valid format (defaults to null).
17568      */
17569     maxValue : null,
17570     /**
17571      * @cfg {String} minText
17572      * The error text to display when the date in the cell is before minValue (defaults to
17573      * 'The date in this field must be after {minValue}').
17574      */
17575     minText : "The date in this field must be equal to or after {0}",
17576     /**
17577      * @cfg {String} maxText
17578      * The error text to display when the date in the cell is after maxValue (defaults to
17579      * 'The date in this field must be before {maxValue}').
17580      */
17581     maxText : "The date in this field must be equal to or before {0}",
17582     /**
17583      * @cfg {String} invalidText
17584      * The error text to display when the date in the field is invalid (defaults to
17585      * '{value} is not a valid date - it must be in the format {format}').
17586      */
17587     invalidText : "{0} is not a valid date - it must be in the format {1}",
17588     /**
17589      * @cfg {String} triggerClass
17590      * An additional CSS class used to style the trigger button.  The trigger will always get the
17591      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17592      * which displays a calendar icon).
17593      */
17594     triggerClass : 'x-form-date-trigger',
17595     
17596
17597     /**
17598      * @cfg {Boolean} useIso
17599      * if enabled, then the date field will use a hidden field to store the 
17600      * real value as iso formated date. default (false)
17601      */ 
17602     useIso : false,
17603     /**
17604      * @cfg {String/Object} autoCreate
17605      * A DomHelper element spec, or true for a default element spec (defaults to
17606      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17607      */ 
17608     // private
17609     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17610     
17611     // private
17612     hiddenField: false,
17613     
17614     onRender : function(ct, position)
17615     {
17616         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17617         if (this.useIso) {
17618             //this.el.dom.removeAttribute('name'); 
17619             Roo.log("Changing name?");
17620             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17621             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17622                     'before', true);
17623             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17624             // prevent input submission
17625             this.hiddenName = this.name;
17626         }
17627             
17628             
17629     },
17630     
17631     // private
17632     validateValue : function(value)
17633     {
17634         value = this.formatDate(value);
17635         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17636             Roo.log('super failed');
17637             return false;
17638         }
17639         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17640              return true;
17641         }
17642         var svalue = value;
17643         value = this.parseDate(value);
17644         if(!value){
17645             Roo.log('parse date failed' + svalue);
17646             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17647             return false;
17648         }
17649         var time = value.getTime();
17650         if(this.minValue && time < this.minValue.getTime()){
17651             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17652             return false;
17653         }
17654         if(this.maxValue && time > this.maxValue.getTime()){
17655             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17656             return false;
17657         }
17658         if(this.disabledDays){
17659             var day = value.getDay();
17660             for(var i = 0; i < this.disabledDays.length; i++) {
17661                 if(day === this.disabledDays[i]){
17662                     this.markInvalid(this.disabledDaysText);
17663                     return false;
17664                 }
17665             }
17666         }
17667         var fvalue = this.formatDate(value);
17668         if(this.ddMatch && this.ddMatch.test(fvalue)){
17669             this.markInvalid(String.format(this.disabledDatesText, fvalue));
17670             return false;
17671         }
17672         return true;
17673     },
17674
17675     // private
17676     // Provides logic to override the default TriggerField.validateBlur which just returns true
17677     validateBlur : function(){
17678         return !this.menu || !this.menu.isVisible();
17679     },
17680     
17681     getName: function()
17682     {
17683         // returns hidden if it's set..
17684         if (!this.rendered) {return ''};
17685         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
17686         
17687     },
17688
17689     /**
17690      * Returns the current date value of the date field.
17691      * @return {Date} The date value
17692      */
17693     getValue : function(){
17694         
17695         return  this.hiddenField ?
17696                 this.hiddenField.value :
17697                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17698     },
17699
17700     /**
17701      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
17702      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17703      * (the default format used is "m/d/y").
17704      * <br />Usage:
17705      * <pre><code>
17706 //All of these calls set the same date value (May 4, 2006)
17707
17708 //Pass a date object:
17709 var dt = new Date('5/4/06');
17710 dateField.setValue(dt);
17711
17712 //Pass a date string (default format):
17713 dateField.setValue('5/4/06');
17714
17715 //Pass a date string (custom format):
17716 dateField.format = 'Y-m-d';
17717 dateField.setValue('2006-5-4');
17718 </code></pre>
17719      * @param {String/Date} date The date or valid date string
17720      */
17721     setValue : function(date){
17722         if (this.hiddenField) {
17723             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
17724         }
17725         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
17726         // make sure the value field is always stored as a date..
17727         this.value = this.parseDate(date);
17728         
17729         
17730     },
17731
17732     // private
17733     parseDate : function(value){
17734         if(!value || value instanceof Date){
17735             return value;
17736         }
17737         var v = Date.parseDate(value, this.format);
17738          if (!v && this.useIso) {
17739             v = Date.parseDate(value, 'Y-m-d');
17740         }
17741         if(!v && this.altFormats){
17742             if(!this.altFormatsArray){
17743                 this.altFormatsArray = this.altFormats.split("|");
17744             }
17745             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17746                 v = Date.parseDate(value, this.altFormatsArray[i]);
17747             }
17748         }
17749         return v;
17750     },
17751
17752     // private
17753     formatDate : function(date, fmt){
17754         return (!date || !(date instanceof Date)) ?
17755                date : date.dateFormat(fmt || this.format);
17756     },
17757
17758     // private
17759     menuListeners : {
17760         select: function(m, d){
17761             
17762             this.setValue(d);
17763             this.fireEvent('select', this, d);
17764         },
17765         show : function(){ // retain focus styling
17766             this.onFocus();
17767         },
17768         hide : function(){
17769             this.focus.defer(10, this);
17770             var ml = this.menuListeners;
17771             this.menu.un("select", ml.select,  this);
17772             this.menu.un("show", ml.show,  this);
17773             this.menu.un("hide", ml.hide,  this);
17774         }
17775     },
17776
17777     // private
17778     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
17779     onTriggerClick : function(){
17780         if(this.disabled){
17781             return;
17782         }
17783         if(this.menu == null){
17784             this.menu = new Roo.menu.DateMenu();
17785         }
17786         Roo.apply(this.menu.picker,  {
17787             showClear: this.allowBlank,
17788             minDate : this.minValue,
17789             maxDate : this.maxValue,
17790             disabledDatesRE : this.ddMatch,
17791             disabledDatesText : this.disabledDatesText,
17792             disabledDays : this.disabledDays,
17793             disabledDaysText : this.disabledDaysText,
17794             format : this.useIso ? 'Y-m-d' : this.format,
17795             minText : String.format(this.minText, this.formatDate(this.minValue)),
17796             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
17797         });
17798         this.menu.on(Roo.apply({}, this.menuListeners, {
17799             scope:this
17800         }));
17801         this.menu.picker.setValue(this.getValue() || new Date());
17802         this.menu.show(this.el, "tl-bl?");
17803     },
17804
17805     beforeBlur : function(){
17806         var v = this.parseDate(this.getRawValue());
17807         if(v){
17808             this.setValue(v);
17809         }
17810     },
17811
17812     /*@
17813      * overide
17814      * 
17815      */
17816     isDirty : function() {
17817         if(this.disabled) {
17818             return false;
17819         }
17820         
17821         if(typeof(this.startValue) === 'undefined'){
17822             return false;
17823         }
17824         
17825         return String(this.getValue()) !== String(this.startValue);
17826         
17827     },
17828     // @overide
17829     cleanLeadingSpace : function(e)
17830     {
17831        return;
17832     }
17833     
17834 });/*
17835  * Based on:
17836  * Ext JS Library 1.1.1
17837  * Copyright(c) 2006-2007, Ext JS, LLC.
17838  *
17839  * Originally Released Under LGPL - original licence link has changed is not relivant.
17840  *
17841  * Fork - LGPL
17842  * <script type="text/javascript">
17843  */
17844  
17845 /**
17846  * @class Roo.form.MonthField
17847  * @extends Roo.form.TriggerField
17848  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17849 * @constructor
17850 * Create a new MonthField
17851 * @param {Object} config
17852  */
17853 Roo.form.MonthField = function(config){
17854     
17855     Roo.form.MonthField.superclass.constructor.call(this, config);
17856     
17857       this.addEvents({
17858          
17859         /**
17860          * @event select
17861          * Fires when a date is selected
17862              * @param {Roo.form.MonthFieeld} combo This combo box
17863              * @param {Date} date The date selected
17864              */
17865         'select' : true
17866          
17867     });
17868     
17869     
17870     if(typeof this.minValue == "string") {
17871         this.minValue = this.parseDate(this.minValue);
17872     }
17873     if(typeof this.maxValue == "string") {
17874         this.maxValue = this.parseDate(this.maxValue);
17875     }
17876     this.ddMatch = null;
17877     if(this.disabledDates){
17878         var dd = this.disabledDates;
17879         var re = "(?:";
17880         for(var i = 0; i < dd.length; i++){
17881             re += dd[i];
17882             if(i != dd.length-1) {
17883                 re += "|";
17884             }
17885         }
17886         this.ddMatch = new RegExp(re + ")");
17887     }
17888 };
17889
17890 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
17891     /**
17892      * @cfg {String} format
17893      * The default date format string which can be overriden for localization support.  The format must be
17894      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17895      */
17896     format : "M Y",
17897     /**
17898      * @cfg {String} altFormats
17899      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17900      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17901      */
17902     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
17903     /**
17904      * @cfg {Array} disabledDays
17905      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17906      */
17907     disabledDays : [0,1,2,3,4,5,6],
17908     /**
17909      * @cfg {String} disabledDaysText
17910      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17911      */
17912     disabledDaysText : "Disabled",
17913     /**
17914      * @cfg {Array} disabledDates
17915      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17916      * expression so they are very powerful. Some examples:
17917      * <ul>
17918      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17919      * <li>["03/08", "09/16"] would disable those days for every year</li>
17920      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17921      * <li>["03/../2006"] would disable every day in March 2006</li>
17922      * <li>["^03"] would disable every day in every March</li>
17923      * </ul>
17924      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17925      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17926      */
17927     disabledDates : null,
17928     /**
17929      * @cfg {String} disabledDatesText
17930      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17931      */
17932     disabledDatesText : "Disabled",
17933     /**
17934      * @cfg {Date/String} minValue
17935      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17936      * valid format (defaults to null).
17937      */
17938     minValue : null,
17939     /**
17940      * @cfg {Date/String} maxValue
17941      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17942      * valid format (defaults to null).
17943      */
17944     maxValue : null,
17945     /**
17946      * @cfg {String} minText
17947      * The error text to display when the date in the cell is before minValue (defaults to
17948      * 'The date in this field must be after {minValue}').
17949      */
17950     minText : "The date in this field must be equal to or after {0}",
17951     /**
17952      * @cfg {String} maxTextf
17953      * The error text to display when the date in the cell is after maxValue (defaults to
17954      * 'The date in this field must be before {maxValue}').
17955      */
17956     maxText : "The date in this field must be equal to or before {0}",
17957     /**
17958      * @cfg {String} invalidText
17959      * The error text to display when the date in the field is invalid (defaults to
17960      * '{value} is not a valid date - it must be in the format {format}').
17961      */
17962     invalidText : "{0} is not a valid date - it must be in the format {1}",
17963     /**
17964      * @cfg {String} triggerClass
17965      * An additional CSS class used to style the trigger button.  The trigger will always get the
17966      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17967      * which displays a calendar icon).
17968      */
17969     triggerClass : 'x-form-date-trigger',
17970     
17971
17972     /**
17973      * @cfg {Boolean} useIso
17974      * if enabled, then the date field will use a hidden field to store the 
17975      * real value as iso formated date. default (true)
17976      */ 
17977     useIso : true,
17978     /**
17979      * @cfg {String/Object} autoCreate
17980      * A DomHelper element spec, or true for a default element spec (defaults to
17981      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17982      */ 
17983     // private
17984     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
17985     
17986     // private
17987     hiddenField: false,
17988     
17989     hideMonthPicker : false,
17990     
17991     onRender : function(ct, position)
17992     {
17993         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
17994         if (this.useIso) {
17995             this.el.dom.removeAttribute('name'); 
17996             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17997                     'before', true);
17998             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17999             // prevent input submission
18000             this.hiddenName = this.name;
18001         }
18002             
18003             
18004     },
18005     
18006     // private
18007     validateValue : function(value)
18008     {
18009         value = this.formatDate(value);
18010         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18011             return false;
18012         }
18013         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18014              return true;
18015         }
18016         var svalue = value;
18017         value = this.parseDate(value);
18018         if(!value){
18019             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18020             return false;
18021         }
18022         var time = value.getTime();
18023         if(this.minValue && time < this.minValue.getTime()){
18024             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18025             return false;
18026         }
18027         if(this.maxValue && time > this.maxValue.getTime()){
18028             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18029             return false;
18030         }
18031         /*if(this.disabledDays){
18032             var day = value.getDay();
18033             for(var i = 0; i < this.disabledDays.length; i++) {
18034                 if(day === this.disabledDays[i]){
18035                     this.markInvalid(this.disabledDaysText);
18036                     return false;
18037                 }
18038             }
18039         }
18040         */
18041         var fvalue = this.formatDate(value);
18042         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18043             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18044             return false;
18045         }
18046         */
18047         return true;
18048     },
18049
18050     // private
18051     // Provides logic to override the default TriggerField.validateBlur which just returns true
18052     validateBlur : function(){
18053         return !this.menu || !this.menu.isVisible();
18054     },
18055
18056     /**
18057      * Returns the current date value of the date field.
18058      * @return {Date} The date value
18059      */
18060     getValue : function(){
18061         
18062         
18063         
18064         return  this.hiddenField ?
18065                 this.hiddenField.value :
18066                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18067     },
18068
18069     /**
18070      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18071      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18072      * (the default format used is "m/d/y").
18073      * <br />Usage:
18074      * <pre><code>
18075 //All of these calls set the same date value (May 4, 2006)
18076
18077 //Pass a date object:
18078 var dt = new Date('5/4/06');
18079 monthField.setValue(dt);
18080
18081 //Pass a date string (default format):
18082 monthField.setValue('5/4/06');
18083
18084 //Pass a date string (custom format):
18085 monthField.format = 'Y-m-d';
18086 monthField.setValue('2006-5-4');
18087 </code></pre>
18088      * @param {String/Date} date The date or valid date string
18089      */
18090     setValue : function(date){
18091         Roo.log('month setValue' + date);
18092         // can only be first of month..
18093         
18094         var val = this.parseDate(date);
18095         
18096         if (this.hiddenField) {
18097             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18098         }
18099         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18100         this.value = this.parseDate(date);
18101     },
18102
18103     // private
18104     parseDate : function(value){
18105         if(!value || value instanceof Date){
18106             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18107             return value;
18108         }
18109         var v = Date.parseDate(value, this.format);
18110         if (!v && this.useIso) {
18111             v = Date.parseDate(value, 'Y-m-d');
18112         }
18113         if (v) {
18114             // 
18115             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18116         }
18117         
18118         
18119         if(!v && this.altFormats){
18120             if(!this.altFormatsArray){
18121                 this.altFormatsArray = this.altFormats.split("|");
18122             }
18123             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18124                 v = Date.parseDate(value, this.altFormatsArray[i]);
18125             }
18126         }
18127         return v;
18128     },
18129
18130     // private
18131     formatDate : function(date, fmt){
18132         return (!date || !(date instanceof Date)) ?
18133                date : date.dateFormat(fmt || this.format);
18134     },
18135
18136     // private
18137     menuListeners : {
18138         select: function(m, d){
18139             this.setValue(d);
18140             this.fireEvent('select', this, d);
18141         },
18142         show : function(){ // retain focus styling
18143             this.onFocus();
18144         },
18145         hide : function(){
18146             this.focus.defer(10, this);
18147             var ml = this.menuListeners;
18148             this.menu.un("select", ml.select,  this);
18149             this.menu.un("show", ml.show,  this);
18150             this.menu.un("hide", ml.hide,  this);
18151         }
18152     },
18153     // private
18154     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18155     onTriggerClick : function(){
18156         if(this.disabled){
18157             return;
18158         }
18159         if(this.menu == null){
18160             this.menu = new Roo.menu.DateMenu();
18161            
18162         }
18163         
18164         Roo.apply(this.menu.picker,  {
18165             
18166             showClear: this.allowBlank,
18167             minDate : this.minValue,
18168             maxDate : this.maxValue,
18169             disabledDatesRE : this.ddMatch,
18170             disabledDatesText : this.disabledDatesText,
18171             
18172             format : this.useIso ? 'Y-m-d' : this.format,
18173             minText : String.format(this.minText, this.formatDate(this.minValue)),
18174             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18175             
18176         });
18177          this.menu.on(Roo.apply({}, this.menuListeners, {
18178             scope:this
18179         }));
18180        
18181         
18182         var m = this.menu;
18183         var p = m.picker;
18184         
18185         // hide month picker get's called when we called by 'before hide';
18186         
18187         var ignorehide = true;
18188         p.hideMonthPicker  = function(disableAnim){
18189             if (ignorehide) {
18190                 return;
18191             }
18192              if(this.monthPicker){
18193                 Roo.log("hideMonthPicker called");
18194                 if(disableAnim === true){
18195                     this.monthPicker.hide();
18196                 }else{
18197                     this.monthPicker.slideOut('t', {duration:.2});
18198                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18199                     p.fireEvent("select", this, this.value);
18200                     m.hide();
18201                 }
18202             }
18203         }
18204         
18205         Roo.log('picker set value');
18206         Roo.log(this.getValue());
18207         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18208         m.show(this.el, 'tl-bl?');
18209         ignorehide  = false;
18210         // this will trigger hideMonthPicker..
18211         
18212         
18213         // hidden the day picker
18214         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18215         
18216         
18217         
18218       
18219         
18220         p.showMonthPicker.defer(100, p);
18221     
18222         
18223        
18224     },
18225
18226     beforeBlur : function(){
18227         var v = this.parseDate(this.getRawValue());
18228         if(v){
18229             this.setValue(v);
18230         }
18231     }
18232
18233     /** @cfg {Boolean} grow @hide */
18234     /** @cfg {Number} growMin @hide */
18235     /** @cfg {Number} growMax @hide */
18236     /**
18237      * @hide
18238      * @method autoSize
18239      */
18240 });/*
18241  * Based on:
18242  * Ext JS Library 1.1.1
18243  * Copyright(c) 2006-2007, Ext JS, LLC.
18244  *
18245  * Originally Released Under LGPL - original licence link has changed is not relivant.
18246  *
18247  * Fork - LGPL
18248  * <script type="text/javascript">
18249  */
18250  
18251
18252 /**
18253  * @class Roo.form.ComboBox
18254  * @extends Roo.form.TriggerField
18255  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18256  * @constructor
18257  * Create a new ComboBox.
18258  * @param {Object} config Configuration options
18259  */
18260 Roo.form.ComboBox = function(config){
18261     Roo.form.ComboBox.superclass.constructor.call(this, config);
18262     this.addEvents({
18263         /**
18264          * @event expand
18265          * Fires when the dropdown list is expanded
18266              * @param {Roo.form.ComboBox} combo This combo box
18267              */
18268         'expand' : true,
18269         /**
18270          * @event collapse
18271          * Fires when the dropdown list is collapsed
18272              * @param {Roo.form.ComboBox} combo This combo box
18273              */
18274         'collapse' : true,
18275         /**
18276          * @event beforeselect
18277          * Fires before a list item is selected. Return false to cancel the selection.
18278              * @param {Roo.form.ComboBox} combo This combo box
18279              * @param {Roo.data.Record} record The data record returned from the underlying store
18280              * @param {Number} index The index of the selected item in the dropdown list
18281              */
18282         'beforeselect' : true,
18283         /**
18284          * @event select
18285          * Fires when a list item is selected
18286              * @param {Roo.form.ComboBox} combo This combo box
18287              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18288              * @param {Number} index The index of the selected item in the dropdown list
18289              */
18290         'select' : true,
18291         /**
18292          * @event beforequery
18293          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18294          * The event object passed has these properties:
18295              * @param {Roo.form.ComboBox} combo This combo box
18296              * @param {String} query The query
18297              * @param {Boolean} forceAll true to force "all" query
18298              * @param {Boolean} cancel true to cancel the query
18299              * @param {Object} e The query event object
18300              */
18301         'beforequery': true,
18302          /**
18303          * @event add
18304          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18305              * @param {Roo.form.ComboBox} combo This combo box
18306              */
18307         'add' : true,
18308         /**
18309          * @event edit
18310          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18311              * @param {Roo.form.ComboBox} combo This combo box
18312              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18313              */
18314         'edit' : true
18315         
18316         
18317     });
18318     if(this.transform){
18319         this.allowDomMove = false;
18320         var s = Roo.getDom(this.transform);
18321         if(!this.hiddenName){
18322             this.hiddenName = s.name;
18323         }
18324         if(!this.store){
18325             this.mode = 'local';
18326             var d = [], opts = s.options;
18327             for(var i = 0, len = opts.length;i < len; i++){
18328                 var o = opts[i];
18329                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18330                 if(o.selected) {
18331                     this.value = value;
18332                 }
18333                 d.push([value, o.text]);
18334             }
18335             this.store = new Roo.data.SimpleStore({
18336                 'id': 0,
18337                 fields: ['value', 'text'],
18338                 data : d
18339             });
18340             this.valueField = 'value';
18341             this.displayField = 'text';
18342         }
18343         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18344         if(!this.lazyRender){
18345             this.target = true;
18346             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18347             s.parentNode.removeChild(s); // remove it
18348             this.render(this.el.parentNode);
18349         }else{
18350             s.parentNode.removeChild(s); // remove it
18351         }
18352
18353     }
18354     if (this.store) {
18355         this.store = Roo.factory(this.store, Roo.data);
18356     }
18357     
18358     this.selectedIndex = -1;
18359     if(this.mode == 'local'){
18360         if(config.queryDelay === undefined){
18361             this.queryDelay = 10;
18362         }
18363         if(config.minChars === undefined){
18364             this.minChars = 0;
18365         }
18366     }
18367 };
18368
18369 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18370     /**
18371      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18372      */
18373     /**
18374      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18375      * rendering into an Roo.Editor, defaults to false)
18376      */
18377     /**
18378      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18379      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18380      */
18381     /**
18382      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18383      */
18384     /**
18385      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18386      * the dropdown list (defaults to undefined, with no header element)
18387      */
18388
18389      /**
18390      * @cfg {String/Roo.Template} tpl The template to use to render the output
18391      */
18392      
18393     // private
18394     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18395     /**
18396      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18397      */
18398     listWidth: undefined,
18399     /**
18400      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18401      * mode = 'remote' or 'text' if mode = 'local')
18402      */
18403     displayField: undefined,
18404     /**
18405      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18406      * mode = 'remote' or 'value' if mode = 'local'). 
18407      * Note: use of a valueField requires the user make a selection
18408      * in order for a value to be mapped.
18409      */
18410     valueField: undefined,
18411     
18412     
18413     /**
18414      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18415      * field's data value (defaults to the underlying DOM element's name)
18416      */
18417     hiddenName: undefined,
18418     /**
18419      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18420      */
18421     listClass: '',
18422     /**
18423      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18424      */
18425     selectedClass: 'x-combo-selected',
18426     /**
18427      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18428      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18429      * which displays a downward arrow icon).
18430      */
18431     triggerClass : 'x-form-arrow-trigger',
18432     /**
18433      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18434      */
18435     shadow:'sides',
18436     /**
18437      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18438      * anchor positions (defaults to 'tl-bl')
18439      */
18440     listAlign: 'tl-bl?',
18441     /**
18442      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18443      */
18444     maxHeight: 300,
18445     /**
18446      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18447      * query specified by the allQuery config option (defaults to 'query')
18448      */
18449     triggerAction: 'query',
18450     /**
18451      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18452      * (defaults to 4, does not apply if editable = false)
18453      */
18454     minChars : 4,
18455     /**
18456      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18457      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18458      */
18459     typeAhead: false,
18460     /**
18461      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18462      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18463      */
18464     queryDelay: 500,
18465     /**
18466      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18467      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18468      */
18469     pageSize: 0,
18470     /**
18471      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18472      * when editable = true (defaults to false)
18473      */
18474     selectOnFocus:false,
18475     /**
18476      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18477      */
18478     queryParam: 'query',
18479     /**
18480      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18481      * when mode = 'remote' (defaults to 'Loading...')
18482      */
18483     loadingText: 'Loading...',
18484     /**
18485      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18486      */
18487     resizable: false,
18488     /**
18489      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18490      */
18491     handleHeight : 8,
18492     /**
18493      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18494      * traditional select (defaults to true)
18495      */
18496     editable: true,
18497     /**
18498      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18499      */
18500     allQuery: '',
18501     /**
18502      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18503      */
18504     mode: 'remote',
18505     /**
18506      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18507      * listWidth has a higher value)
18508      */
18509     minListWidth : 70,
18510     /**
18511      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18512      * allow the user to set arbitrary text into the field (defaults to false)
18513      */
18514     forceSelection:false,
18515     /**
18516      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18517      * if typeAhead = true (defaults to 250)
18518      */
18519     typeAheadDelay : 250,
18520     /**
18521      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18522      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18523      */
18524     valueNotFoundText : undefined,
18525     /**
18526      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18527      */
18528     blockFocus : false,
18529     
18530     /**
18531      * @cfg {Boolean} disableClear Disable showing of clear button.
18532      */
18533     disableClear : false,
18534     /**
18535      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18536      */
18537     alwaysQuery : false,
18538     
18539     //private
18540     addicon : false,
18541     editicon: false,
18542     
18543     // element that contains real text value.. (when hidden is used..)
18544      
18545     // private
18546     onRender : function(ct, position)
18547     {
18548         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18549         
18550         if(this.hiddenName){
18551             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18552                     'before', true);
18553             this.hiddenField.value =
18554                 this.hiddenValue !== undefined ? this.hiddenValue :
18555                 this.value !== undefined ? this.value : '';
18556
18557             // prevent input submission
18558             this.el.dom.removeAttribute('name');
18559              
18560              
18561         }
18562         
18563         if(Roo.isGecko){
18564             this.el.dom.setAttribute('autocomplete', 'off');
18565         }
18566
18567         var cls = 'x-combo-list';
18568
18569         this.list = new Roo.Layer({
18570             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18571         });
18572
18573         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18574         this.list.setWidth(lw);
18575         this.list.swallowEvent('mousewheel');
18576         this.assetHeight = 0;
18577
18578         if(this.title){
18579             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18580             this.assetHeight += this.header.getHeight();
18581         }
18582
18583         this.innerList = this.list.createChild({cls:cls+'-inner'});
18584         this.innerList.on('mouseover', this.onViewOver, this);
18585         this.innerList.on('mousemove', this.onViewMove, this);
18586         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18587         
18588         if(this.allowBlank && !this.pageSize && !this.disableClear){
18589             this.footer = this.list.createChild({cls:cls+'-ft'});
18590             this.pageTb = new Roo.Toolbar(this.footer);
18591            
18592         }
18593         if(this.pageSize){
18594             this.footer = this.list.createChild({cls:cls+'-ft'});
18595             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18596                     {pageSize: this.pageSize});
18597             
18598         }
18599         
18600         if (this.pageTb && this.allowBlank && !this.disableClear) {
18601             var _this = this;
18602             this.pageTb.add(new Roo.Toolbar.Fill(), {
18603                 cls: 'x-btn-icon x-btn-clear',
18604                 text: '&#160;',
18605                 handler: function()
18606                 {
18607                     _this.collapse();
18608                     _this.clearValue();
18609                     _this.onSelect(false, -1);
18610                 }
18611             });
18612         }
18613         if (this.footer) {
18614             this.assetHeight += this.footer.getHeight();
18615         }
18616         
18617
18618         if(!this.tpl){
18619             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18620         }
18621
18622         this.view = new Roo.View(this.innerList, this.tpl, {
18623             singleSelect:true,
18624             store: this.store,
18625             selectedClass: this.selectedClass
18626         });
18627
18628         this.view.on('click', this.onViewClick, this);
18629
18630         this.store.on('beforeload', this.onBeforeLoad, this);
18631         this.store.on('load', this.onLoad, this);
18632         this.store.on('loadexception', this.onLoadException, this);
18633
18634         if(this.resizable){
18635             this.resizer = new Roo.Resizable(this.list,  {
18636                pinned:true, handles:'se'
18637             });
18638             this.resizer.on('resize', function(r, w, h){
18639                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18640                 this.listWidth = w;
18641                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18642                 this.restrictHeight();
18643             }, this);
18644             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18645         }
18646         if(!this.editable){
18647             this.editable = true;
18648             this.setEditable(false);
18649         }  
18650         
18651         
18652         if (typeof(this.events.add.listeners) != 'undefined') {
18653             
18654             this.addicon = this.wrap.createChild(
18655                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
18656        
18657             this.addicon.on('click', function(e) {
18658                 this.fireEvent('add', this);
18659             }, this);
18660         }
18661         if (typeof(this.events.edit.listeners) != 'undefined') {
18662             
18663             this.editicon = this.wrap.createChild(
18664                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
18665             if (this.addicon) {
18666                 this.editicon.setStyle('margin-left', '40px');
18667             }
18668             this.editicon.on('click', function(e) {
18669                 
18670                 // we fire even  if inothing is selected..
18671                 this.fireEvent('edit', this, this.lastData );
18672                 
18673             }, this);
18674         }
18675         
18676         
18677         
18678     },
18679
18680     // private
18681     initEvents : function(){
18682         Roo.form.ComboBox.superclass.initEvents.call(this);
18683
18684         this.keyNav = new Roo.KeyNav(this.el, {
18685             "up" : function(e){
18686                 this.inKeyMode = true;
18687                 this.selectPrev();
18688             },
18689
18690             "down" : function(e){
18691                 if(!this.isExpanded()){
18692                     this.onTriggerClick();
18693                 }else{
18694                     this.inKeyMode = true;
18695                     this.selectNext();
18696                 }
18697             },
18698
18699             "enter" : function(e){
18700                 this.onViewClick();
18701                 //return true;
18702             },
18703
18704             "esc" : function(e){
18705                 this.collapse();
18706             },
18707
18708             "tab" : function(e){
18709                 this.onViewClick(false);
18710                 this.fireEvent("specialkey", this, e);
18711                 return true;
18712             },
18713
18714             scope : this,
18715
18716             doRelay : function(foo, bar, hname){
18717                 if(hname == 'down' || this.scope.isExpanded()){
18718                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18719                 }
18720                 return true;
18721             },
18722
18723             forceKeyDown: true
18724         });
18725         this.queryDelay = Math.max(this.queryDelay || 10,
18726                 this.mode == 'local' ? 10 : 250);
18727         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18728         if(this.typeAhead){
18729             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18730         }
18731         if(this.editable !== false){
18732             this.el.on("keyup", this.onKeyUp, this);
18733         }
18734         if(this.forceSelection){
18735             this.on('blur', this.doForce, this);
18736         }
18737     },
18738
18739     onDestroy : function(){
18740         if(this.view){
18741             this.view.setStore(null);
18742             this.view.el.removeAllListeners();
18743             this.view.el.remove();
18744             this.view.purgeListeners();
18745         }
18746         if(this.list){
18747             this.list.destroy();
18748         }
18749         if(this.store){
18750             this.store.un('beforeload', this.onBeforeLoad, this);
18751             this.store.un('load', this.onLoad, this);
18752             this.store.un('loadexception', this.onLoadException, this);
18753         }
18754         Roo.form.ComboBox.superclass.onDestroy.call(this);
18755     },
18756
18757     // private
18758     fireKey : function(e){
18759         if(e.isNavKeyPress() && !this.list.isVisible()){
18760             this.fireEvent("specialkey", this, e);
18761         }
18762     },
18763
18764     // private
18765     onResize: function(w, h){
18766         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
18767         
18768         if(typeof w != 'number'){
18769             // we do not handle it!?!?
18770             return;
18771         }
18772         var tw = this.trigger.getWidth();
18773         tw += this.addicon ? this.addicon.getWidth() : 0;
18774         tw += this.editicon ? this.editicon.getWidth() : 0;
18775         var x = w - tw;
18776         this.el.setWidth( this.adjustWidth('input', x));
18777             
18778         this.trigger.setStyle('left', x+'px');
18779         
18780         if(this.list && this.listWidth === undefined){
18781             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
18782             this.list.setWidth(lw);
18783             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18784         }
18785         
18786     
18787         
18788     },
18789
18790     /**
18791      * Allow or prevent the user from directly editing the field text.  If false is passed,
18792      * the user will only be able to select from the items defined in the dropdown list.  This method
18793      * is the runtime equivalent of setting the 'editable' config option at config time.
18794      * @param {Boolean} value True to allow the user to directly edit the field text
18795      */
18796     setEditable : function(value){
18797         if(value == this.editable){
18798             return;
18799         }
18800         this.editable = value;
18801         if(!value){
18802             this.el.dom.setAttribute('readOnly', true);
18803             this.el.on('mousedown', this.onTriggerClick,  this);
18804             this.el.addClass('x-combo-noedit');
18805         }else{
18806             this.el.dom.setAttribute('readOnly', false);
18807             this.el.un('mousedown', this.onTriggerClick,  this);
18808             this.el.removeClass('x-combo-noedit');
18809         }
18810     },
18811
18812     // private
18813     onBeforeLoad : function(){
18814         if(!this.hasFocus){
18815             return;
18816         }
18817         this.innerList.update(this.loadingText ?
18818                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
18819         this.restrictHeight();
18820         this.selectedIndex = -1;
18821     },
18822
18823     // private
18824     onLoad : function(){
18825         if(!this.hasFocus){
18826             return;
18827         }
18828         if(this.store.getCount() > 0){
18829             this.expand();
18830             this.restrictHeight();
18831             if(this.lastQuery == this.allQuery){
18832                 if(this.editable){
18833                     this.el.dom.select();
18834                 }
18835                 if(!this.selectByValue(this.value, true)){
18836                     this.select(0, true);
18837                 }
18838             }else{
18839                 this.selectNext();
18840                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18841                     this.taTask.delay(this.typeAheadDelay);
18842                 }
18843             }
18844         }else{
18845             this.onEmptyResults();
18846         }
18847         //this.el.focus();
18848     },
18849     // private
18850     onLoadException : function()
18851     {
18852         this.collapse();
18853         Roo.log(this.store.reader.jsonData);
18854         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18855             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18856         }
18857         
18858         
18859     },
18860     // private
18861     onTypeAhead : function(){
18862         if(this.store.getCount() > 0){
18863             var r = this.store.getAt(0);
18864             var newValue = r.data[this.displayField];
18865             var len = newValue.length;
18866             var selStart = this.getRawValue().length;
18867             if(selStart != len){
18868                 this.setRawValue(newValue);
18869                 this.selectText(selStart, newValue.length);
18870             }
18871         }
18872     },
18873
18874     // private
18875     onSelect : function(record, index){
18876         if(this.fireEvent('beforeselect', this, record, index) !== false){
18877             this.setFromData(index > -1 ? record.data : false);
18878             this.collapse();
18879             this.fireEvent('select', this, record, index);
18880         }
18881     },
18882
18883     /**
18884      * Returns the currently selected field value or empty string if no value is set.
18885      * @return {String} value The selected value
18886      */
18887     getValue : function(){
18888         if(this.valueField){
18889             return typeof this.value != 'undefined' ? this.value : '';
18890         }
18891         return Roo.form.ComboBox.superclass.getValue.call(this);
18892     },
18893
18894     /**
18895      * Clears any text/value currently set in the field
18896      */
18897     clearValue : function(){
18898         if(this.hiddenField){
18899             this.hiddenField.value = '';
18900         }
18901         this.value = '';
18902         this.setRawValue('');
18903         this.lastSelectionText = '';
18904         
18905     },
18906
18907     /**
18908      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18909      * will be displayed in the field.  If the value does not match the data value of an existing item,
18910      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18911      * Otherwise the field will be blank (although the value will still be set).
18912      * @param {String} value The value to match
18913      */
18914     setValue : function(v){
18915         var text = v;
18916         if(this.valueField){
18917             var r = this.findRecord(this.valueField, v);
18918             if(r){
18919                 text = r.data[this.displayField];
18920             }else if(this.valueNotFoundText !== undefined){
18921                 text = this.valueNotFoundText;
18922             }
18923         }
18924         this.lastSelectionText = text;
18925         if(this.hiddenField){
18926             this.hiddenField.value = v;
18927         }
18928         Roo.form.ComboBox.superclass.setValue.call(this, text);
18929         this.value = v;
18930     },
18931     /**
18932      * @property {Object} the last set data for the element
18933      */
18934     
18935     lastData : false,
18936     /**
18937      * Sets the value of the field based on a object which is related to the record format for the store.
18938      * @param {Object} value the value to set as. or false on reset?
18939      */
18940     setFromData : function(o){
18941         var dv = ''; // display value
18942         var vv = ''; // value value..
18943         this.lastData = o;
18944         if (this.displayField) {
18945             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18946         } else {
18947             // this is an error condition!!!
18948             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18949         }
18950         
18951         if(this.valueField){
18952             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18953         }
18954         if(this.hiddenField){
18955             this.hiddenField.value = vv;
18956             
18957             this.lastSelectionText = dv;
18958             Roo.form.ComboBox.superclass.setValue.call(this, dv);
18959             this.value = vv;
18960             return;
18961         }
18962         // no hidden field.. - we store the value in 'value', but still display
18963         // display field!!!!
18964         this.lastSelectionText = dv;
18965         Roo.form.ComboBox.superclass.setValue.call(this, dv);
18966         this.value = vv;
18967         
18968         
18969     },
18970     // private
18971     reset : function(){
18972         // overridden so that last data is reset..
18973         this.setValue(this.resetValue);
18974         this.originalValue = this.getValue();
18975         this.clearInvalid();
18976         this.lastData = false;
18977         if (this.view) {
18978             this.view.clearSelections();
18979         }
18980     },
18981     // private
18982     findRecord : function(prop, value){
18983         var record;
18984         if(this.store.getCount() > 0){
18985             this.store.each(function(r){
18986                 if(r.data[prop] == value){
18987                     record = r;
18988                     return false;
18989                 }
18990                 return true;
18991             });
18992         }
18993         return record;
18994     },
18995     
18996     getName: function()
18997     {
18998         // returns hidden if it's set..
18999         if (!this.rendered) {return ''};
19000         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
19001         
19002     },
19003     // private
19004     onViewMove : function(e, t){
19005         this.inKeyMode = false;
19006     },
19007
19008     // private
19009     onViewOver : function(e, t){
19010         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19011             return;
19012         }
19013         var item = this.view.findItemFromChild(t);
19014         if(item){
19015             var index = this.view.indexOf(item);
19016             this.select(index, false);
19017         }
19018     },
19019
19020     // private
19021     onViewClick : function(doFocus)
19022     {
19023         var index = this.view.getSelectedIndexes()[0];
19024         var r = this.store.getAt(index);
19025         if(r){
19026             this.onSelect(r, index);
19027         }
19028         if(doFocus !== false && !this.blockFocus){
19029             this.el.focus();
19030         }
19031     },
19032
19033     // private
19034     restrictHeight : function(){
19035         this.innerList.dom.style.height = '';
19036         var inner = this.innerList.dom;
19037         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19038         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19039         this.list.beginUpdate();
19040         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19041         this.list.alignTo(this.el, this.listAlign);
19042         this.list.endUpdate();
19043     },
19044
19045     // private
19046     onEmptyResults : function(){
19047         this.collapse();
19048     },
19049
19050     /**
19051      * Returns true if the dropdown list is expanded, else false.
19052      */
19053     isExpanded : function(){
19054         return this.list.isVisible();
19055     },
19056
19057     /**
19058      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19059      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19060      * @param {String} value The data value of the item to select
19061      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19062      * selected item if it is not currently in view (defaults to true)
19063      * @return {Boolean} True if the value matched an item in the list, else false
19064      */
19065     selectByValue : function(v, scrollIntoView){
19066         if(v !== undefined && v !== null){
19067             var r = this.findRecord(this.valueField || this.displayField, v);
19068             if(r){
19069                 this.select(this.store.indexOf(r), scrollIntoView);
19070                 return true;
19071             }
19072         }
19073         return false;
19074     },
19075
19076     /**
19077      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19078      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19079      * @param {Number} index The zero-based index of the list item to select
19080      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19081      * selected item if it is not currently in view (defaults to true)
19082      */
19083     select : function(index, scrollIntoView){
19084         this.selectedIndex = index;
19085         this.view.select(index);
19086         if(scrollIntoView !== false){
19087             var el = this.view.getNode(index);
19088             if(el){
19089                 this.innerList.scrollChildIntoView(el, false);
19090             }
19091         }
19092     },
19093
19094     // private
19095     selectNext : function(){
19096         var ct = this.store.getCount();
19097         if(ct > 0){
19098             if(this.selectedIndex == -1){
19099                 this.select(0);
19100             }else if(this.selectedIndex < ct-1){
19101                 this.select(this.selectedIndex+1);
19102             }
19103         }
19104     },
19105
19106     // private
19107     selectPrev : function(){
19108         var ct = this.store.getCount();
19109         if(ct > 0){
19110             if(this.selectedIndex == -1){
19111                 this.select(0);
19112             }else if(this.selectedIndex != 0){
19113                 this.select(this.selectedIndex-1);
19114             }
19115         }
19116     },
19117
19118     // private
19119     onKeyUp : function(e){
19120         if(this.editable !== false && !e.isSpecialKey()){
19121             this.lastKey = e.getKey();
19122             this.dqTask.delay(this.queryDelay);
19123         }
19124     },
19125
19126     // private
19127     validateBlur : function(){
19128         return !this.list || !this.list.isVisible();   
19129     },
19130
19131     // private
19132     initQuery : function(){
19133         this.doQuery(this.getRawValue());
19134     },
19135
19136     // private
19137     doForce : function(){
19138         if(this.el.dom.value.length > 0){
19139             this.el.dom.value =
19140                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19141              
19142         }
19143     },
19144
19145     /**
19146      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19147      * query allowing the query action to be canceled if needed.
19148      * @param {String} query The SQL query to execute
19149      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19150      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19151      * saved in the current store (defaults to false)
19152      */
19153     doQuery : function(q, forceAll){
19154         if(q === undefined || q === null){
19155             q = '';
19156         }
19157         var qe = {
19158             query: q,
19159             forceAll: forceAll,
19160             combo: this,
19161             cancel:false
19162         };
19163         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19164             return false;
19165         }
19166         q = qe.query;
19167         forceAll = qe.forceAll;
19168         if(forceAll === true || (q.length >= this.minChars)){
19169             if(this.lastQuery != q || this.alwaysQuery){
19170                 this.lastQuery = q;
19171                 if(this.mode == 'local'){
19172                     this.selectedIndex = -1;
19173                     if(forceAll){
19174                         this.store.clearFilter();
19175                     }else{
19176                         this.store.filter(this.displayField, q);
19177                     }
19178                     this.onLoad();
19179                 }else{
19180                     this.store.baseParams[this.queryParam] = q;
19181                     this.store.load({
19182                         params: this.getParams(q)
19183                     });
19184                     this.expand();
19185                 }
19186             }else{
19187                 this.selectedIndex = -1;
19188                 this.onLoad();   
19189             }
19190         }
19191     },
19192
19193     // private
19194     getParams : function(q){
19195         var p = {};
19196         //p[this.queryParam] = q;
19197         if(this.pageSize){
19198             p.start = 0;
19199             p.limit = this.pageSize;
19200         }
19201         return p;
19202     },
19203
19204     /**
19205      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19206      */
19207     collapse : function(){
19208         if(!this.isExpanded()){
19209             return;
19210         }
19211         this.list.hide();
19212         Roo.get(document).un('mousedown', this.collapseIf, this);
19213         Roo.get(document).un('mousewheel', this.collapseIf, this);
19214         if (!this.editable) {
19215             Roo.get(document).un('keydown', this.listKeyPress, this);
19216         }
19217         this.fireEvent('collapse', this);
19218     },
19219
19220     // private
19221     collapseIf : function(e){
19222         if(!e.within(this.wrap) && !e.within(this.list)){
19223             this.collapse();
19224         }
19225     },
19226
19227     /**
19228      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19229      */
19230     expand : function(){
19231         if(this.isExpanded() || !this.hasFocus){
19232             return;
19233         }
19234         this.list.alignTo(this.el, this.listAlign);
19235         this.list.show();
19236         Roo.get(document).on('mousedown', this.collapseIf, this);
19237         Roo.get(document).on('mousewheel', this.collapseIf, this);
19238         if (!this.editable) {
19239             Roo.get(document).on('keydown', this.listKeyPress, this);
19240         }
19241         
19242         this.fireEvent('expand', this);
19243     },
19244
19245     // private
19246     // Implements the default empty TriggerField.onTriggerClick function
19247     onTriggerClick : function(){
19248         if(this.disabled){
19249             return;
19250         }
19251         if(this.isExpanded()){
19252             this.collapse();
19253             if (!this.blockFocus) {
19254                 this.el.focus();
19255             }
19256             
19257         }else {
19258             this.hasFocus = true;
19259             if(this.triggerAction == 'all') {
19260                 this.doQuery(this.allQuery, true);
19261             } else {
19262                 this.doQuery(this.getRawValue());
19263             }
19264             if (!this.blockFocus) {
19265                 this.el.focus();
19266             }
19267         }
19268     },
19269     listKeyPress : function(e)
19270     {
19271         //Roo.log('listkeypress');
19272         // scroll to first matching element based on key pres..
19273         if (e.isSpecialKey()) {
19274             return false;
19275         }
19276         var k = String.fromCharCode(e.getKey()).toUpperCase();
19277         //Roo.log(k);
19278         var match  = false;
19279         var csel = this.view.getSelectedNodes();
19280         var cselitem = false;
19281         if (csel.length) {
19282             var ix = this.view.indexOf(csel[0]);
19283             cselitem  = this.store.getAt(ix);
19284             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19285                 cselitem = false;
19286             }
19287             
19288         }
19289         
19290         this.store.each(function(v) { 
19291             if (cselitem) {
19292                 // start at existing selection.
19293                 if (cselitem.id == v.id) {
19294                     cselitem = false;
19295                 }
19296                 return;
19297             }
19298                 
19299             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19300                 match = this.store.indexOf(v);
19301                 return false;
19302             }
19303         }, this);
19304         
19305         if (match === false) {
19306             return true; // no more action?
19307         }
19308         // scroll to?
19309         this.view.select(match);
19310         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19311         sn.scrollIntoView(sn.dom.parentNode, false);
19312     } 
19313
19314     /** 
19315     * @cfg {Boolean} grow 
19316     * @hide 
19317     */
19318     /** 
19319     * @cfg {Number} growMin 
19320     * @hide 
19321     */
19322     /** 
19323     * @cfg {Number} growMax 
19324     * @hide 
19325     */
19326     /**
19327      * @hide
19328      * @method autoSize
19329      */
19330 });/*
19331  * Copyright(c) 2010-2012, Roo J Solutions Limited
19332  *
19333  * Licence LGPL
19334  *
19335  */
19336
19337 /**
19338  * @class Roo.form.ComboBoxArray
19339  * @extends Roo.form.TextField
19340  * A facebook style adder... for lists of email / people / countries  etc...
19341  * pick multiple items from a combo box, and shows each one.
19342  *
19343  *  Fred [x]  Brian [x]  [Pick another |v]
19344  *
19345  *
19346  *  For this to work: it needs various extra information
19347  *    - normal combo problay has
19348  *      name, hiddenName
19349  *    + displayField, valueField
19350  *
19351  *    For our purpose...
19352  *
19353  *
19354  *   If we change from 'extends' to wrapping...
19355  *   
19356  *  
19357  *
19358  
19359  
19360  * @constructor
19361  * Create a new ComboBoxArray.
19362  * @param {Object} config Configuration options
19363  */
19364  
19365
19366 Roo.form.ComboBoxArray = function(config)
19367 {
19368     this.addEvents({
19369         /**
19370          * @event beforeremove
19371          * Fires before remove the value from the list
19372              * @param {Roo.form.ComboBoxArray} _self This combo box array
19373              * @param {Roo.form.ComboBoxArray.Item} item removed item
19374              */
19375         'beforeremove' : true,
19376         /**
19377          * @event remove
19378          * Fires when remove the value from the list
19379              * @param {Roo.form.ComboBoxArray} _self This combo box array
19380              * @param {Roo.form.ComboBoxArray.Item} item removed item
19381              */
19382         'remove' : true
19383         
19384         
19385     });
19386     
19387     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19388     
19389     this.items = new Roo.util.MixedCollection(false);
19390     
19391     // construct the child combo...
19392     
19393     
19394     
19395     
19396    
19397     
19398 }
19399
19400  
19401 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19402
19403     /**
19404      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
19405      */
19406     
19407     lastData : false,
19408     
19409     // behavies liek a hiddne field
19410     inputType:      'hidden',
19411     /**
19412      * @cfg {Number} width The width of the box that displays the selected element
19413      */ 
19414     width:          300,
19415
19416     
19417     
19418     /**
19419      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19420      */
19421     name : false,
19422     /**
19423      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19424      */
19425     hiddenName : false,
19426       /**
19427      * @cfg {String} seperator    The value seperator normally ',' 
19428      */
19429     seperator : ',',
19430     
19431     // private the array of items that are displayed..
19432     items  : false,
19433     // private - the hidden field el.
19434     hiddenEl : false,
19435     // private - the filed el..
19436     el : false,
19437     
19438     //validateValue : function() { return true; }, // all values are ok!
19439     //onAddClick: function() { },
19440     
19441     onRender : function(ct, position) 
19442     {
19443         
19444         // create the standard hidden element
19445         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19446         
19447         
19448         // give fake names to child combo;
19449         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19450         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19451         
19452         this.combo = Roo.factory(this.combo, Roo.form);
19453         this.combo.onRender(ct, position);
19454         if (typeof(this.combo.width) != 'undefined') {
19455             this.combo.onResize(this.combo.width,0);
19456         }
19457         
19458         this.combo.initEvents();
19459         
19460         // assigned so form know we need to do this..
19461         this.store          = this.combo.store;
19462         this.valueField     = this.combo.valueField;
19463         this.displayField   = this.combo.displayField ;
19464         
19465         
19466         this.combo.wrap.addClass('x-cbarray-grp');
19467         
19468         var cbwrap = this.combo.wrap.createChild(
19469             {tag: 'div', cls: 'x-cbarray-cb'},
19470             this.combo.el.dom
19471         );
19472         
19473              
19474         this.hiddenEl = this.combo.wrap.createChild({
19475             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19476         });
19477         this.el = this.combo.wrap.createChild({
19478             tag: 'input',  type:'hidden' , name: this.name, value : ''
19479         });
19480          //   this.el.dom.removeAttribute("name");
19481         
19482         
19483         this.outerWrap = this.combo.wrap;
19484         this.wrap = cbwrap;
19485         
19486         this.outerWrap.setWidth(this.width);
19487         this.outerWrap.dom.removeChild(this.el.dom);
19488         
19489         this.wrap.dom.appendChild(this.el.dom);
19490         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19491         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19492         
19493         this.combo.trigger.setStyle('position','relative');
19494         this.combo.trigger.setStyle('left', '0px');
19495         this.combo.trigger.setStyle('top', '2px');
19496         
19497         this.combo.el.setStyle('vertical-align', 'text-bottom');
19498         
19499         //this.trigger.setStyle('vertical-align', 'top');
19500         
19501         // this should use the code from combo really... on('add' ....)
19502         if (this.adder) {
19503             
19504         
19505             this.adder = this.outerWrap.createChild(
19506                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19507             var _t = this;
19508             this.adder.on('click', function(e) {
19509                 _t.fireEvent('adderclick', this, e);
19510             }, _t);
19511         }
19512         //var _t = this;
19513         //this.adder.on('click', this.onAddClick, _t);
19514         
19515         
19516         this.combo.on('select', function(cb, rec, ix) {
19517             this.addItem(rec.data);
19518             
19519             cb.setValue('');
19520             cb.el.dom.value = '';
19521             //cb.lastData = rec.data;
19522             // add to list
19523             
19524         }, this);
19525         
19526         
19527     },
19528     
19529     
19530     getName: function()
19531     {
19532         // returns hidden if it's set..
19533         if (!this.rendered) {return ''};
19534         return  this.hiddenName ? this.hiddenName : this.name;
19535         
19536     },
19537     
19538     
19539     onResize: function(w, h){
19540         
19541         return;
19542         // not sure if this is needed..
19543         //this.combo.onResize(w,h);
19544         
19545         if(typeof w != 'number'){
19546             // we do not handle it!?!?
19547             return;
19548         }
19549         var tw = this.combo.trigger.getWidth();
19550         tw += this.addicon ? this.addicon.getWidth() : 0;
19551         tw += this.editicon ? this.editicon.getWidth() : 0;
19552         var x = w - tw;
19553         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19554             
19555         this.combo.trigger.setStyle('left', '0px');
19556         
19557         if(this.list && this.listWidth === undefined){
19558             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19559             this.list.setWidth(lw);
19560             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19561         }
19562         
19563     
19564         
19565     },
19566     
19567     addItem: function(rec)
19568     {
19569         var valueField = this.combo.valueField;
19570         var displayField = this.combo.displayField;
19571         
19572         if (this.items.indexOfKey(rec[valueField]) > -1) {
19573             //console.log("GOT " + rec.data.id);
19574             return;
19575         }
19576         
19577         var x = new Roo.form.ComboBoxArray.Item({
19578             //id : rec[this.idField],
19579             data : rec,
19580             displayField : displayField ,
19581             tipField : displayField ,
19582             cb : this
19583         });
19584         // use the 
19585         this.items.add(rec[valueField],x);
19586         // add it before the element..
19587         this.updateHiddenEl();
19588         x.render(this.outerWrap, this.wrap.dom);
19589         // add the image handler..
19590     },
19591     
19592     updateHiddenEl : function()
19593     {
19594         this.validate();
19595         if (!this.hiddenEl) {
19596             return;
19597         }
19598         var ar = [];
19599         var idField = this.combo.valueField;
19600         
19601         this.items.each(function(f) {
19602             ar.push(f.data[idField]);
19603         });
19604         this.hiddenEl.dom.value = ar.join(this.seperator);
19605         this.validate();
19606     },
19607     
19608     reset : function()
19609     {
19610         this.items.clear();
19611         
19612         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19613            el.remove();
19614         });
19615         
19616         this.el.dom.value = '';
19617         if (this.hiddenEl) {
19618             this.hiddenEl.dom.value = '';
19619         }
19620         
19621     },
19622     getValue: function()
19623     {
19624         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19625     },
19626     setValue: function(v) // not a valid action - must use addItems..
19627     {
19628         
19629         this.reset();
19630          
19631         if (this.store.isLocal && (typeof(v) == 'string')) {
19632             // then we can use the store to find the values..
19633             // comma seperated at present.. this needs to allow JSON based encoding..
19634             this.hiddenEl.value  = v;
19635             var v_ar = [];
19636             Roo.each(v.split(this.seperator), function(k) {
19637                 Roo.log("CHECK " + this.valueField + ',' + k);
19638                 var li = this.store.query(this.valueField, k);
19639                 if (!li.length) {
19640                     return;
19641                 }
19642                 var add = {};
19643                 add[this.valueField] = k;
19644                 add[this.displayField] = li.item(0).data[this.displayField];
19645                 
19646                 this.addItem(add);
19647             }, this) 
19648              
19649         }
19650         if (typeof(v) == 'object' ) {
19651             // then let's assume it's an array of objects..
19652             Roo.each(v, function(l) {
19653                 var add = l;
19654                 if (typeof(l) == 'string') {
19655                     add = {};
19656                     add[this.valueField] = l;
19657                     add[this.displayField] = l
19658                 }
19659                 this.addItem(add);
19660             }, this);
19661              
19662         }
19663         
19664         
19665     },
19666     setFromData: function(v)
19667     {
19668         // this recieves an object, if setValues is called.
19669         this.reset();
19670         this.el.dom.value = v[this.displayField];
19671         this.hiddenEl.dom.value = v[this.valueField];
19672         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19673             return;
19674         }
19675         var kv = v[this.valueField];
19676         var dv = v[this.displayField];
19677         kv = typeof(kv) != 'string' ? '' : kv;
19678         dv = typeof(dv) != 'string' ? '' : dv;
19679         
19680         
19681         var keys = kv.split(this.seperator);
19682         var display = dv.split(this.seperator);
19683         for (var i = 0 ; i < keys.length; i++) {
19684             add = {};
19685             add[this.valueField] = keys[i];
19686             add[this.displayField] = display[i];
19687             this.addItem(add);
19688         }
19689       
19690         
19691     },
19692     
19693     /**
19694      * Validates the combox array value
19695      * @return {Boolean} True if the value is valid, else false
19696      */
19697     validate : function(){
19698         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19699             this.clearInvalid();
19700             return true;
19701         }
19702         return false;
19703     },
19704     
19705     validateValue : function(value){
19706         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19707         
19708     },
19709     
19710     /*@
19711      * overide
19712      * 
19713      */
19714     isDirty : function() {
19715         if(this.disabled) {
19716             return false;
19717         }
19718         
19719         try {
19720             var d = Roo.decode(String(this.originalValue));
19721         } catch (e) {
19722             return String(this.getValue()) !== String(this.originalValue);
19723         }
19724         
19725         var originalValue = [];
19726         
19727         for (var i = 0; i < d.length; i++){
19728             originalValue.push(d[i][this.valueField]);
19729         }
19730         
19731         return String(this.getValue()) !== String(originalValue.join(this.seperator));
19732         
19733     }
19734     
19735 });
19736
19737
19738
19739 /**
19740  * @class Roo.form.ComboBoxArray.Item
19741  * @extends Roo.BoxComponent
19742  * A selected item in the list
19743  *  Fred [x]  Brian [x]  [Pick another |v]
19744  * 
19745  * @constructor
19746  * Create a new item.
19747  * @param {Object} config Configuration options
19748  */
19749  
19750 Roo.form.ComboBoxArray.Item = function(config) {
19751     config.id = Roo.id();
19752     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
19753 }
19754
19755 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
19756     data : {},
19757     cb: false,
19758     displayField : false,
19759     tipField : false,
19760     
19761     
19762     defaultAutoCreate : {
19763         tag: 'div',
19764         cls: 'x-cbarray-item',
19765         cn : [ 
19766             { tag: 'div' },
19767             {
19768                 tag: 'img',
19769                 width:16,
19770                 height : 16,
19771                 src : Roo.BLANK_IMAGE_URL ,
19772                 align: 'center'
19773             }
19774         ]
19775         
19776     },
19777     
19778  
19779     onRender : function(ct, position)
19780     {
19781         Roo.form.Field.superclass.onRender.call(this, ct, position);
19782         
19783         if(!this.el){
19784             var cfg = this.getAutoCreate();
19785             this.el = ct.createChild(cfg, position);
19786         }
19787         
19788         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
19789         
19790         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
19791             this.cb.renderer(this.data) :
19792             String.format('{0}',this.data[this.displayField]);
19793         
19794             
19795         this.el.child('div').dom.setAttribute('qtip',
19796                         String.format('{0}',this.data[this.tipField])
19797         );
19798         
19799         this.el.child('img').on('click', this.remove, this);
19800         
19801     },
19802    
19803     remove : function()
19804     {
19805         if(this.cb.disabled){
19806             return;
19807         }
19808         
19809         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
19810             this.cb.items.remove(this);
19811             this.el.child('img').un('click', this.remove, this);
19812             this.el.remove();
19813             this.cb.updateHiddenEl();
19814
19815             this.cb.fireEvent('remove', this.cb, this);
19816         }
19817         
19818     }
19819 });/*
19820  * RooJS Library 1.1.1
19821  * Copyright(c) 2008-2011  Alan Knowles
19822  *
19823  * License - LGPL
19824  */
19825  
19826
19827 /**
19828  * @class Roo.form.ComboNested
19829  * @extends Roo.form.ComboBox
19830  * A combobox for that allows selection of nested items in a list,
19831  * eg.
19832  *
19833  *  Book
19834  *    -> red
19835  *    -> green
19836  *  Table
19837  *    -> square
19838  *      ->red
19839  *      ->green
19840  *    -> rectangle
19841  *      ->green
19842  *      
19843  * 
19844  * @constructor
19845  * Create a new ComboNested
19846  * @param {Object} config Configuration options
19847  */
19848 Roo.form.ComboNested = function(config){
19849     Roo.form.ComboCheck.superclass.constructor.call(this, config);
19850     // should verify some data...
19851     // like
19852     // hiddenName = required..
19853     // displayField = required
19854     // valudField == required
19855     var req= [ 'hiddenName', 'displayField', 'valueField' ];
19856     var _t = this;
19857     Roo.each(req, function(e) {
19858         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
19859             throw "Roo.form.ComboNested : missing value for: " + e;
19860         }
19861     });
19862      
19863     
19864 };
19865
19866 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
19867    
19868     /*
19869      * @config {Number} max Number of columns to show
19870      */
19871     
19872     maxColumns : 3,
19873    
19874     list : null, // the outermost div..
19875     innerLists : null, // the
19876     views : null,
19877     stores : null,
19878     // private
19879     loadingChildren : false,
19880     
19881     onRender : function(ct, position)
19882     {
19883         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
19884         
19885         if(this.hiddenName){
19886             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
19887                     'before', true);
19888             this.hiddenField.value =
19889                 this.hiddenValue !== undefined ? this.hiddenValue :
19890                 this.value !== undefined ? this.value : '';
19891
19892             // prevent input submission
19893             this.el.dom.removeAttribute('name');
19894              
19895              
19896         }
19897         
19898         if(Roo.isGecko){
19899             this.el.dom.setAttribute('autocomplete', 'off');
19900         }
19901
19902         var cls = 'x-combo-list';
19903
19904         this.list = new Roo.Layer({
19905             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
19906         });
19907
19908         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
19909         this.list.setWidth(lw);
19910         this.list.swallowEvent('mousewheel');
19911         this.assetHeight = 0;
19912
19913         if(this.title){
19914             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
19915             this.assetHeight += this.header.getHeight();
19916         }
19917         this.innerLists = [];
19918         this.views = [];
19919         this.stores = [];
19920         for (var i =0 ; i < this.maxColumns; i++) {
19921             this.onRenderList( cls, i);
19922         }
19923         
19924         // always needs footer, as we are going to have an 'OK' button.
19925         this.footer = this.list.createChild({cls:cls+'-ft'});
19926         this.pageTb = new Roo.Toolbar(this.footer);  
19927         var _this = this;
19928         this.pageTb.add(  {
19929             
19930             text: 'Done',
19931             handler: function()
19932             {
19933                 _this.collapse();
19934             }
19935         });
19936         
19937         if ( this.allowBlank && !this.disableClear) {
19938             
19939             this.pageTb.add(new Roo.Toolbar.Fill(), {
19940                 cls: 'x-btn-icon x-btn-clear',
19941                 text: '&#160;',
19942                 handler: function()
19943                 {
19944                     _this.collapse();
19945                     _this.clearValue();
19946                     _this.onSelect(false, -1);
19947                 }
19948             });
19949         }
19950         if (this.footer) {
19951             this.assetHeight += this.footer.getHeight();
19952         }
19953         
19954     },
19955     onRenderList : function (  cls, i)
19956     {
19957         
19958         var lw = Math.floor(
19959                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
19960         );
19961         
19962         this.list.setWidth(lw); // default to '1'
19963
19964         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
19965         //il.on('mouseover', this.onViewOver, this, { list:  i });
19966         //il.on('mousemove', this.onViewMove, this, { list:  i });
19967         il.setWidth(lw);
19968         il.setStyle({ 'overflow-x' : 'hidden'});
19969
19970         if(!this.tpl){
19971             this.tpl = new Roo.Template({
19972                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
19973                 isEmpty: function (value, allValues) {
19974                     //Roo.log(value);
19975                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
19976                     return dl ? 'has-children' : 'no-children'
19977                 }
19978             });
19979         }
19980         
19981         var store  = this.store;
19982         if (i > 0) {
19983             store  = new Roo.data.SimpleStore({
19984                 //fields : this.store.reader.meta.fields,
19985                 reader : this.store.reader,
19986                 data : [ ]
19987             });
19988         }
19989         this.stores[i]  = store;
19990                   
19991         var view = this.views[i] = new Roo.View(
19992             il,
19993             this.tpl,
19994             {
19995                 singleSelect:true,
19996                 store: store,
19997                 selectedClass: this.selectedClass
19998             }
19999         );
20000         view.getEl().setWidth(lw);
20001         view.getEl().setStyle({
20002             position: i < 1 ? 'relative' : 'absolute',
20003             top: 0,
20004             left: (i * lw ) + 'px',
20005             display : i > 0 ? 'none' : 'block'
20006         });
20007         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
20008         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
20009         //view.on('click', this.onViewClick, this, { list : i });
20010
20011         store.on('beforeload', this.onBeforeLoad, this);
20012         store.on('load',  this.onLoad, this, { list  : i});
20013         store.on('loadexception', this.onLoadException, this);
20014
20015         // hide the other vies..
20016         
20017         
20018         
20019     },
20020       
20021     restrictHeight : function()
20022     {
20023         var mh = 0;
20024         Roo.each(this.innerLists, function(il,i) {
20025             var el = this.views[i].getEl();
20026             el.dom.style.height = '';
20027             var inner = el.dom;
20028             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
20029             // only adjust heights on other ones..
20030             mh = Math.max(h, mh);
20031             if (i < 1) {
20032                 
20033                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20034                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20035                
20036             }
20037             
20038             
20039         }, this);
20040         
20041         this.list.beginUpdate();
20042         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20043         this.list.alignTo(this.el, this.listAlign);
20044         this.list.endUpdate();
20045         
20046     },
20047      
20048     
20049     // -- store handlers..
20050     // private
20051     onBeforeLoad : function()
20052     {
20053         if(!this.hasFocus){
20054             return;
20055         }
20056         this.innerLists[0].update(this.loadingText ?
20057                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20058         this.restrictHeight();
20059         this.selectedIndex = -1;
20060     },
20061     // private
20062     onLoad : function(a,b,c,d)
20063     {
20064         if (!this.loadingChildren) {
20065             // then we are loading the top level. - hide the children
20066             for (var i = 1;i < this.views.length; i++) {
20067                 this.views[i].getEl().setStyle({ display : 'none' });
20068             }
20069             var lw = Math.floor(
20070                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20071             );
20072         
20073              this.list.setWidth(lw); // default to '1'
20074
20075             
20076         }
20077         if(!this.hasFocus){
20078             return;
20079         }
20080         
20081         if(this.store.getCount() > 0) {
20082             this.expand();
20083             this.restrictHeight();   
20084         } else {
20085             this.onEmptyResults();
20086         }
20087         
20088         if (!this.loadingChildren) {
20089             this.selectActive();
20090         }
20091         /*
20092         this.stores[1].loadData([]);
20093         this.stores[2].loadData([]);
20094         this.views
20095         */    
20096     
20097         //this.el.focus();
20098     },
20099     
20100     
20101     // private
20102     onLoadException : function()
20103     {
20104         this.collapse();
20105         Roo.log(this.store.reader.jsonData);
20106         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20107             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20108         }
20109         
20110         
20111     },
20112     // no cleaning of leading spaces on blur here.
20113     cleanLeadingSpace : function(e) { },
20114     
20115
20116     onSelectChange : function (view, sels, opts )
20117     {
20118         var ix = view.getSelectedIndexes();
20119          
20120         if (opts.list > this.maxColumns - 2) {
20121             if (view.store.getCount()<  1) {
20122                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
20123
20124             } else  {
20125                 if (ix.length) {
20126                     // used to clear ?? but if we are loading unselected 
20127                     this.setFromData(view.store.getAt(ix[0]).data);
20128                 }
20129                 
20130             }
20131             
20132             return;
20133         }
20134         
20135         if (!ix.length) {
20136             // this get's fired when trigger opens..
20137            // this.setFromData({});
20138             var str = this.stores[opts.list+1];
20139             str.data.clear(); // removeall wihtout the fire events..
20140             return;
20141         }
20142         
20143         var rec = view.store.getAt(ix[0]);
20144          
20145         this.setFromData(rec.data);
20146         this.fireEvent('select', this, rec, ix[0]);
20147         
20148         var lw = Math.floor(
20149              (
20150                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
20151              ) / this.maxColumns
20152         );
20153         this.loadingChildren = true;
20154         this.stores[opts.list+1].loadDataFromChildren( rec );
20155         this.loadingChildren = false;
20156         var dl = this.stores[opts.list+1]. getTotalCount();
20157         
20158         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20159         
20160         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20161         for (var i = opts.list+2; i < this.views.length;i++) {
20162             this.views[i].getEl().setStyle({ display : 'none' });
20163         }
20164         
20165         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20166         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
20167         
20168         if (this.isLoading) {
20169            // this.selectActive(opts.list);
20170         }
20171          
20172     },
20173     
20174     
20175     
20176     
20177     onDoubleClick : function()
20178     {
20179         this.collapse(); //??
20180     },
20181     
20182      
20183     
20184     
20185     
20186     // private
20187     recordToStack : function(store, prop, value, stack)
20188     {
20189         var cstore = new Roo.data.SimpleStore({
20190             //fields : this.store.reader.meta.fields, // we need array reader.. for
20191             reader : this.store.reader,
20192             data : [ ]
20193         });
20194         var _this = this;
20195         var record  = false;
20196         var srec = false;
20197         if(store.getCount() < 1){
20198             return false;
20199         }
20200         store.each(function(r){
20201             if(r.data[prop] == value){
20202                 record = r;
20203             srec = r;
20204                 return false;
20205             }
20206             if (r.data.cn && r.data.cn.length) {
20207                 cstore.loadDataFromChildren( r);
20208                 var cret = _this.recordToStack(cstore, prop, value, stack);
20209                 if (cret !== false) {
20210                     record = cret;
20211                     srec = r;
20212                     return false;
20213                 }
20214             }
20215              
20216             return true;
20217         });
20218         if (record == false) {
20219             return false
20220         }
20221         stack.unshift(srec);
20222         return record;
20223     },
20224     
20225     /*
20226      * find the stack of stores that match our value.
20227      *
20228      * 
20229      */
20230     
20231     selectActive : function ()
20232     {
20233         // if store is not loaded, then we will need to wait for that to happen first.
20234         var stack = [];
20235         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
20236         for (var i = 0; i < stack.length; i++ ) {
20237             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
20238         }
20239         
20240     }
20241         
20242          
20243     
20244     
20245     
20246     
20247 });/*
20248  * Based on:
20249  * Ext JS Library 1.1.1
20250  * Copyright(c) 2006-2007, Ext JS, LLC.
20251  *
20252  * Originally Released Under LGPL - original licence link has changed is not relivant.
20253  *
20254  * Fork - LGPL
20255  * <script type="text/javascript">
20256  */
20257 /**
20258  * @class Roo.form.Checkbox
20259  * @extends Roo.form.Field
20260  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20261  * @constructor
20262  * Creates a new Checkbox
20263  * @param {Object} config Configuration options
20264  */
20265 Roo.form.Checkbox = function(config){
20266     Roo.form.Checkbox.superclass.constructor.call(this, config);
20267     this.addEvents({
20268         /**
20269          * @event check
20270          * Fires when the checkbox is checked or unchecked.
20271              * @param {Roo.form.Checkbox} this This checkbox
20272              * @param {Boolean} checked The new checked value
20273              */
20274         check : true
20275     });
20276 };
20277
20278 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20279     /**
20280      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20281      */
20282     focusClass : undefined,
20283     /**
20284      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20285      */
20286     fieldClass: "x-form-field",
20287     /**
20288      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20289      */
20290     checked: false,
20291     /**
20292      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20293      * {tag: "input", type: "checkbox", autocomplete: "off"})
20294      */
20295     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20296     /**
20297      * @cfg {String} boxLabel The text that appears beside the checkbox
20298      */
20299     boxLabel : "",
20300     /**
20301      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20302      */  
20303     inputValue : '1',
20304     /**
20305      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20306      */
20307      valueOff: '0', // value when not checked..
20308
20309     actionMode : 'viewEl', 
20310     //
20311     // private
20312     itemCls : 'x-menu-check-item x-form-item',
20313     groupClass : 'x-menu-group-item',
20314     inputType : 'hidden',
20315     
20316     
20317     inSetChecked: false, // check that we are not calling self...
20318     
20319     inputElement: false, // real input element?
20320     basedOn: false, // ????
20321     
20322     isFormField: true, // not sure where this is needed!!!!
20323
20324     onResize : function(){
20325         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20326         if(!this.boxLabel){
20327             this.el.alignTo(this.wrap, 'c-c');
20328         }
20329     },
20330
20331     initEvents : function(){
20332         Roo.form.Checkbox.superclass.initEvents.call(this);
20333         this.el.on("click", this.onClick,  this);
20334         this.el.on("change", this.onClick,  this);
20335     },
20336
20337
20338     getResizeEl : function(){
20339         return this.wrap;
20340     },
20341
20342     getPositionEl : function(){
20343         return this.wrap;
20344     },
20345
20346     // private
20347     onRender : function(ct, position){
20348         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20349         /*
20350         if(this.inputValue !== undefined){
20351             this.el.dom.value = this.inputValue;
20352         }
20353         */
20354         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20355         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20356         var viewEl = this.wrap.createChild({ 
20357             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20358         this.viewEl = viewEl;   
20359         this.wrap.on('click', this.onClick,  this); 
20360         
20361         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20362         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20363         
20364         
20365         
20366         if(this.boxLabel){
20367             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20368         //    viewEl.on('click', this.onClick,  this); 
20369         }
20370         //if(this.checked){
20371             this.setChecked(this.checked);
20372         //}else{
20373             //this.checked = this.el.dom;
20374         //}
20375
20376     },
20377
20378     // private
20379     initValue : Roo.emptyFn,
20380
20381     /**
20382      * Returns the checked state of the checkbox.
20383      * @return {Boolean} True if checked, else false
20384      */
20385     getValue : function(){
20386         if(this.el){
20387             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20388         }
20389         return this.valueOff;
20390         
20391     },
20392
20393         // private
20394     onClick : function(){ 
20395         if (this.disabled) {
20396             return;
20397         }
20398         this.setChecked(!this.checked);
20399
20400         //if(this.el.dom.checked != this.checked){
20401         //    this.setValue(this.el.dom.checked);
20402        // }
20403     },
20404
20405     /**
20406      * Sets the checked state of the checkbox.
20407      * On is always based on a string comparison between inputValue and the param.
20408      * @param {Boolean/String} value - the value to set 
20409      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20410      */
20411     setValue : function(v,suppressEvent){
20412         
20413         
20414         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20415         //if(this.el && this.el.dom){
20416         //    this.el.dom.checked = this.checked;
20417         //    this.el.dom.defaultChecked = this.checked;
20418         //}
20419         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20420         //this.fireEvent("check", this, this.checked);
20421     },
20422     // private..
20423     setChecked : function(state,suppressEvent)
20424     {
20425         if (this.inSetChecked) {
20426             this.checked = state;
20427             return;
20428         }
20429         
20430     
20431         if(this.wrap){
20432             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20433         }
20434         this.checked = state;
20435         if(suppressEvent !== true){
20436             this.fireEvent('check', this, state);
20437         }
20438         this.inSetChecked = true;
20439         this.el.dom.value = state ? this.inputValue : this.valueOff;
20440         this.inSetChecked = false;
20441         
20442     },
20443     // handle setting of hidden value by some other method!!?!?
20444     setFromHidden: function()
20445     {
20446         if(!this.el){
20447             return;
20448         }
20449         //console.log("SET FROM HIDDEN");
20450         //alert('setFrom hidden');
20451         this.setValue(this.el.dom.value);
20452     },
20453     
20454     onDestroy : function()
20455     {
20456         if(this.viewEl){
20457             Roo.get(this.viewEl).remove();
20458         }
20459          
20460         Roo.form.Checkbox.superclass.onDestroy.call(this);
20461     },
20462     
20463     setBoxLabel : function(str)
20464     {
20465         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20466     }
20467
20468 });/*
20469  * Based on:
20470  * Ext JS Library 1.1.1
20471  * Copyright(c) 2006-2007, Ext JS, LLC.
20472  *
20473  * Originally Released Under LGPL - original licence link has changed is not relivant.
20474  *
20475  * Fork - LGPL
20476  * <script type="text/javascript">
20477  */
20478  
20479 /**
20480  * @class Roo.form.Radio
20481  * @extends Roo.form.Checkbox
20482  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20483  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20484  * @constructor
20485  * Creates a new Radio
20486  * @param {Object} config Configuration options
20487  */
20488 Roo.form.Radio = function(){
20489     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20490 };
20491 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20492     inputType: 'radio',
20493
20494     /**
20495      * If this radio is part of a group, it will return the selected value
20496      * @return {String}
20497      */
20498     getGroupValue : function(){
20499         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20500     },
20501     
20502     
20503     onRender : function(ct, position){
20504         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20505         
20506         if(this.inputValue !== undefined){
20507             this.el.dom.value = this.inputValue;
20508         }
20509          
20510         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20511         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20512         //var viewEl = this.wrap.createChild({ 
20513         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20514         //this.viewEl = viewEl;   
20515         //this.wrap.on('click', this.onClick,  this); 
20516         
20517         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20518         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20519         
20520         
20521         
20522         if(this.boxLabel){
20523             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20524         //    viewEl.on('click', this.onClick,  this); 
20525         }
20526          if(this.checked){
20527             this.el.dom.checked =   'checked' ;
20528         }
20529          
20530     } 
20531     
20532     
20533 });//<script type="text/javascript">
20534
20535 /*
20536  * Based  Ext JS Library 1.1.1
20537  * Copyright(c) 2006-2007, Ext JS, LLC.
20538  * LGPL
20539  *
20540  */
20541  
20542 /**
20543  * @class Roo.HtmlEditorCore
20544  * @extends Roo.Component
20545  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20546  *
20547  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20548  */
20549
20550 Roo.HtmlEditorCore = function(config){
20551     
20552     
20553     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20554     
20555     
20556     this.addEvents({
20557         /**
20558          * @event initialize
20559          * Fires when the editor is fully initialized (including the iframe)
20560          * @param {Roo.HtmlEditorCore} this
20561          */
20562         initialize: true,
20563         /**
20564          * @event activate
20565          * Fires when the editor is first receives the focus. Any insertion must wait
20566          * until after this event.
20567          * @param {Roo.HtmlEditorCore} this
20568          */
20569         activate: true,
20570          /**
20571          * @event beforesync
20572          * Fires before the textarea is updated with content from the editor iframe. Return false
20573          * to cancel the sync.
20574          * @param {Roo.HtmlEditorCore} this
20575          * @param {String} html
20576          */
20577         beforesync: true,
20578          /**
20579          * @event beforepush
20580          * Fires before the iframe editor is updated with content from the textarea. Return false
20581          * to cancel the push.
20582          * @param {Roo.HtmlEditorCore} this
20583          * @param {String} html
20584          */
20585         beforepush: true,
20586          /**
20587          * @event sync
20588          * Fires when the textarea is updated with content from the editor iframe.
20589          * @param {Roo.HtmlEditorCore} this
20590          * @param {String} html
20591          */
20592         sync: true,
20593          /**
20594          * @event push
20595          * Fires when the iframe editor is updated with content from the textarea.
20596          * @param {Roo.HtmlEditorCore} this
20597          * @param {String} html
20598          */
20599         push: true,
20600         
20601         /**
20602          * @event editorevent
20603          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20604          * @param {Roo.HtmlEditorCore} this
20605          */
20606         editorevent: true
20607         
20608     });
20609     
20610     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20611     
20612     // defaults : white / black...
20613     this.applyBlacklists();
20614     
20615     
20616     
20617 };
20618
20619
20620 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20621
20622
20623      /**
20624      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20625      */
20626     
20627     owner : false,
20628     
20629      /**
20630      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20631      *                        Roo.resizable.
20632      */
20633     resizable : false,
20634      /**
20635      * @cfg {Number} height (in pixels)
20636      */   
20637     height: 300,
20638    /**
20639      * @cfg {Number} width (in pixels)
20640      */   
20641     width: 500,
20642     
20643     /**
20644      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20645      * 
20646      */
20647     stylesheets: false,
20648     
20649     /**
20650      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
20651      */
20652     allowComments: false,
20653     // id of frame..
20654     frameId: false,
20655     
20656     // private properties
20657     validationEvent : false,
20658     deferHeight: true,
20659     initialized : false,
20660     activated : false,
20661     sourceEditMode : false,
20662     onFocus : Roo.emptyFn,
20663     iframePad:3,
20664     hideMode:'offsets',
20665     
20666     clearUp: true,
20667     
20668     // blacklist + whitelisted elements..
20669     black: false,
20670     white: false,
20671      
20672     bodyCls : '',
20673
20674     /**
20675      * Protected method that will not generally be called directly. It
20676      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20677      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20678      */
20679     getDocMarkup : function(){
20680         // body styles..
20681         var st = '';
20682         
20683         // inherit styels from page...?? 
20684         if (this.stylesheets === false) {
20685             
20686             Roo.get(document.head).select('style').each(function(node) {
20687                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20688             });
20689             
20690             Roo.get(document.head).select('link').each(function(node) { 
20691                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20692             });
20693             
20694         } else if (!this.stylesheets.length) {
20695                 // simple..
20696                 st = '<style type="text/css">' +
20697                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20698                    '</style>';
20699         } else {
20700             for (var i in this.stylesheets) { 
20701                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
20702             }
20703             
20704         }
20705         
20706         st +=  '<style type="text/css">' +
20707             'IMG { cursor: pointer } ' +
20708         '</style>';
20709
20710         var cls = 'roo-htmleditor-body';
20711         
20712         if(this.bodyCls.length){
20713             cls += ' ' + this.bodyCls;
20714         }
20715         
20716         return '<html><head>' + st  +
20717             //<style type="text/css">' +
20718             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20719             //'</style>' +
20720             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
20721     },
20722
20723     // private
20724     onRender : function(ct, position)
20725     {
20726         var _t = this;
20727         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20728         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20729         
20730         
20731         this.el.dom.style.border = '0 none';
20732         this.el.dom.setAttribute('tabIndex', -1);
20733         this.el.addClass('x-hidden hide');
20734         
20735         
20736         
20737         if(Roo.isIE){ // fix IE 1px bogus margin
20738             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20739         }
20740        
20741         
20742         this.frameId = Roo.id();
20743         
20744          
20745         
20746         var iframe = this.owner.wrap.createChild({
20747             tag: 'iframe',
20748             cls: 'form-control', // bootstrap..
20749             id: this.frameId,
20750             name: this.frameId,
20751             frameBorder : 'no',
20752             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20753         }, this.el
20754         );
20755         
20756         
20757         this.iframe = iframe.dom;
20758
20759          this.assignDocWin();
20760         
20761         this.doc.designMode = 'on';
20762        
20763         this.doc.open();
20764         this.doc.write(this.getDocMarkup());
20765         this.doc.close();
20766
20767         
20768         var task = { // must defer to wait for browser to be ready
20769             run : function(){
20770                 //console.log("run task?" + this.doc.readyState);
20771                 this.assignDocWin();
20772                 if(this.doc.body || this.doc.readyState == 'complete'){
20773                     try {
20774                         this.doc.designMode="on";
20775                     } catch (e) {
20776                         return;
20777                     }
20778                     Roo.TaskMgr.stop(task);
20779                     this.initEditor.defer(10, this);
20780                 }
20781             },
20782             interval : 10,
20783             duration: 10000,
20784             scope: this
20785         };
20786         Roo.TaskMgr.start(task);
20787
20788     },
20789
20790     // private
20791     onResize : function(w, h)
20792     {
20793          Roo.log('resize: ' +w + ',' + h );
20794         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20795         if(!this.iframe){
20796             return;
20797         }
20798         if(typeof w == 'number'){
20799             
20800             this.iframe.style.width = w + 'px';
20801         }
20802         if(typeof h == 'number'){
20803             
20804             this.iframe.style.height = h + 'px';
20805             if(this.doc){
20806                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20807             }
20808         }
20809         
20810     },
20811
20812     /**
20813      * Toggles the editor between standard and source edit mode.
20814      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20815      */
20816     toggleSourceEdit : function(sourceEditMode){
20817         
20818         this.sourceEditMode = sourceEditMode === true;
20819         
20820         if(this.sourceEditMode){
20821  
20822             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20823             
20824         }else{
20825             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20826             //this.iframe.className = '';
20827             this.deferFocus();
20828         }
20829         //this.setSize(this.owner.wrap.getSize());
20830         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20831     },
20832
20833     
20834   
20835
20836     /**
20837      * Protected method that will not generally be called directly. If you need/want
20838      * custom HTML cleanup, this is the method you should override.
20839      * @param {String} html The HTML to be cleaned
20840      * return {String} The cleaned HTML
20841      */
20842     cleanHtml : function(html){
20843         html = String(html);
20844         if(html.length > 5){
20845             if(Roo.isSafari){ // strip safari nonsense
20846                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20847             }
20848         }
20849         if(html == '&nbsp;'){
20850             html = '';
20851         }
20852         return html;
20853     },
20854
20855     /**
20856      * HTML Editor -> Textarea
20857      * Protected method that will not generally be called directly. Syncs the contents
20858      * of the editor iframe with the textarea.
20859      */
20860     syncValue : function(){
20861         if(this.initialized){
20862             var bd = (this.doc.body || this.doc.documentElement);
20863             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20864             var html = bd.innerHTML;
20865             if(Roo.isSafari){
20866                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20867                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20868                 if(m && m[1]){
20869                     html = '<div style="'+m[0]+'">' + html + '</div>';
20870                 }
20871             }
20872             html = this.cleanHtml(html);
20873             // fix up the special chars.. normaly like back quotes in word...
20874             // however we do not want to do this with chinese..
20875             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
20876                 
20877                 var cc = match.charCodeAt();
20878
20879                 // Get the character value, handling surrogate pairs
20880                 if (match.length == 2) {
20881                     // It's a surrogate pair, calculate the Unicode code point
20882                     var high = match.charCodeAt(0) - 0xD800;
20883                     var low  = match.charCodeAt(1) - 0xDC00;
20884                     cc = (high * 0x400) + low + 0x10000;
20885                 }  else if (
20886                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20887                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20888                     (cc >= 0xf900 && cc < 0xfb00 )
20889                 ) {
20890                         return match;
20891                 }  
20892          
20893                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
20894                 return "&#" + cc + ";";
20895                 
20896                 
20897             });
20898             
20899             
20900              
20901             if(this.owner.fireEvent('beforesync', this, html) !== false){
20902                 this.el.dom.value = html;
20903                 this.owner.fireEvent('sync', this, html);
20904             }
20905         }
20906     },
20907
20908     /**
20909      * Protected method that will not generally be called directly. Pushes the value of the textarea
20910      * into the iframe editor.
20911      */
20912     pushValue : function(){
20913         if(this.initialized){
20914             var v = this.el.dom.value.trim();
20915             
20916 //            if(v.length < 1){
20917 //                v = '&#160;';
20918 //            }
20919             
20920             if(this.owner.fireEvent('beforepush', this, v) !== false){
20921                 var d = (this.doc.body || this.doc.documentElement);
20922                 d.innerHTML = v;
20923                 this.cleanUpPaste();
20924                 this.el.dom.value = d.innerHTML;
20925                 this.owner.fireEvent('push', this, v);
20926             }
20927         }
20928     },
20929
20930     // private
20931     deferFocus : function(){
20932         this.focus.defer(10, this);
20933     },
20934
20935     // doc'ed in Field
20936     focus : function(){
20937         if(this.win && !this.sourceEditMode){
20938             this.win.focus();
20939         }else{
20940             this.el.focus();
20941         }
20942     },
20943     
20944     assignDocWin: function()
20945     {
20946         var iframe = this.iframe;
20947         
20948          if(Roo.isIE){
20949             this.doc = iframe.contentWindow.document;
20950             this.win = iframe.contentWindow;
20951         } else {
20952 //            if (!Roo.get(this.frameId)) {
20953 //                return;
20954 //            }
20955 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20956 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20957             
20958             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20959                 return;
20960             }
20961             
20962             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20963             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20964         }
20965     },
20966     
20967     // private
20968     initEditor : function(){
20969         //console.log("INIT EDITOR");
20970         this.assignDocWin();
20971         
20972         
20973         
20974         this.doc.designMode="on";
20975         this.doc.open();
20976         this.doc.write(this.getDocMarkup());
20977         this.doc.close();
20978         
20979         var dbody = (this.doc.body || this.doc.documentElement);
20980         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20981         // this copies styles from the containing element into thsi one..
20982         // not sure why we need all of this..
20983         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20984         
20985         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20986         //ss['background-attachment'] = 'fixed'; // w3c
20987         dbody.bgProperties = 'fixed'; // ie
20988         //Roo.DomHelper.applyStyles(dbody, ss);
20989         Roo.EventManager.on(this.doc, {
20990             //'mousedown': this.onEditorEvent,
20991             'mouseup': this.onEditorEvent,
20992             'dblclick': this.onEditorEvent,
20993             'click': this.onEditorEvent,
20994             'keyup': this.onEditorEvent,
20995             buffer:100,
20996             scope: this
20997         });
20998         if(Roo.isGecko){
20999             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21000         }
21001         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21002             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21003         }
21004         this.initialized = true;
21005
21006         this.owner.fireEvent('initialize', this);
21007         this.pushValue();
21008     },
21009
21010     // private
21011     onDestroy : function(){
21012         
21013         
21014         
21015         if(this.rendered){
21016             
21017             //for (var i =0; i < this.toolbars.length;i++) {
21018             //    // fixme - ask toolbars for heights?
21019             //    this.toolbars[i].onDestroy();
21020            // }
21021             
21022             //this.wrap.dom.innerHTML = '';
21023             //this.wrap.remove();
21024         }
21025     },
21026
21027     // private
21028     onFirstFocus : function(){
21029         
21030         this.assignDocWin();
21031         
21032         
21033         this.activated = true;
21034          
21035     
21036         if(Roo.isGecko){ // prevent silly gecko errors
21037             this.win.focus();
21038             var s = this.win.getSelection();
21039             if(!s.focusNode || s.focusNode.nodeType != 3){
21040                 var r = s.getRangeAt(0);
21041                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21042                 r.collapse(true);
21043                 this.deferFocus();
21044             }
21045             try{
21046                 this.execCmd('useCSS', true);
21047                 this.execCmd('styleWithCSS', false);
21048             }catch(e){}
21049         }
21050         this.owner.fireEvent('activate', this);
21051     },
21052
21053     // private
21054     adjustFont: function(btn){
21055         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21056         //if(Roo.isSafari){ // safari
21057         //    adjust *= 2;
21058        // }
21059         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21060         if(Roo.isSafari){ // safari
21061             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21062             v =  (v < 10) ? 10 : v;
21063             v =  (v > 48) ? 48 : v;
21064             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21065             
21066         }
21067         
21068         
21069         v = Math.max(1, v+adjust);
21070         
21071         this.execCmd('FontSize', v  );
21072     },
21073
21074     onEditorEvent : function(e)
21075     {
21076         this.owner.fireEvent('editorevent', this, e);
21077       //  this.updateToolbar();
21078         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21079     },
21080
21081     insertTag : function(tg)
21082     {
21083         // could be a bit smarter... -> wrap the current selected tRoo..
21084         if (tg.toLowerCase() == 'span' ||
21085             tg.toLowerCase() == 'code' ||
21086             tg.toLowerCase() == 'sup' ||
21087             tg.toLowerCase() == 'sub' 
21088             ) {
21089             
21090             range = this.createRange(this.getSelection());
21091             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21092             wrappingNode.appendChild(range.extractContents());
21093             range.insertNode(wrappingNode);
21094
21095             return;
21096             
21097             
21098             
21099         }
21100         this.execCmd("formatblock",   tg);
21101         
21102     },
21103     
21104     insertText : function(txt)
21105     {
21106         
21107         
21108         var range = this.createRange();
21109         range.deleteContents();
21110                //alert(Sender.getAttribute('label'));
21111                
21112         range.insertNode(this.doc.createTextNode(txt));
21113     } ,
21114     
21115      
21116
21117     /**
21118      * Executes a Midas editor command on the editor document and performs necessary focus and
21119      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21120      * @param {String} cmd The Midas command
21121      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21122      */
21123     relayCmd : function(cmd, value){
21124         this.win.focus();
21125         this.execCmd(cmd, value);
21126         this.owner.fireEvent('editorevent', this);
21127         //this.updateToolbar();
21128         this.owner.deferFocus();
21129     },
21130
21131     /**
21132      * Executes a Midas editor command directly on the editor document.
21133      * For visual commands, you should use {@link #relayCmd} instead.
21134      * <b>This should only be called after the editor is initialized.</b>
21135      * @param {String} cmd The Midas command
21136      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21137      */
21138     execCmd : function(cmd, value){
21139         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21140         this.syncValue();
21141     },
21142  
21143  
21144    
21145     /**
21146      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21147      * to insert tRoo.
21148      * @param {String} text | dom node.. 
21149      */
21150     insertAtCursor : function(text)
21151     {
21152         
21153         if(!this.activated){
21154             return;
21155         }
21156         /*
21157         if(Roo.isIE){
21158             this.win.focus();
21159             var r = this.doc.selection.createRange();
21160             if(r){
21161                 r.collapse(true);
21162                 r.pasteHTML(text);
21163                 this.syncValue();
21164                 this.deferFocus();
21165             
21166             }
21167             return;
21168         }
21169         */
21170         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21171             this.win.focus();
21172             
21173             
21174             // from jquery ui (MIT licenced)
21175             var range, node;
21176             var win = this.win;
21177             
21178             if (win.getSelection && win.getSelection().getRangeAt) {
21179                 range = win.getSelection().getRangeAt(0);
21180                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21181                 range.insertNode(node);
21182             } else if (win.document.selection && win.document.selection.createRange) {
21183                 // no firefox support
21184                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21185                 win.document.selection.createRange().pasteHTML(txt);
21186             } else {
21187                 // no firefox support
21188                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21189                 this.execCmd('InsertHTML', txt);
21190             } 
21191             
21192             this.syncValue();
21193             
21194             this.deferFocus();
21195         }
21196     },
21197  // private
21198     mozKeyPress : function(e){
21199         if(e.ctrlKey){
21200             var c = e.getCharCode(), cmd;
21201           
21202             if(c > 0){
21203                 c = String.fromCharCode(c).toLowerCase();
21204                 switch(c){
21205                     case 'b':
21206                         cmd = 'bold';
21207                         break;
21208                     case 'i':
21209                         cmd = 'italic';
21210                         break;
21211                     
21212                     case 'u':
21213                         cmd = 'underline';
21214                         break;
21215                     
21216                     case 'v':
21217                         this.cleanUpPaste.defer(100, this);
21218                         return;
21219                         
21220                 }
21221                 if(cmd){
21222                     this.win.focus();
21223                     this.execCmd(cmd);
21224                     this.deferFocus();
21225                     e.preventDefault();
21226                 }
21227                 
21228             }
21229         }
21230     },
21231
21232     // private
21233     fixKeys : function(){ // load time branching for fastest keydown performance
21234         if(Roo.isIE){
21235             return function(e){
21236                 var k = e.getKey(), r;
21237                 if(k == e.TAB){
21238                     e.stopEvent();
21239                     r = this.doc.selection.createRange();
21240                     if(r){
21241                         r.collapse(true);
21242                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21243                         this.deferFocus();
21244                     }
21245                     return;
21246                 }
21247                 
21248                 if(k == e.ENTER){
21249                     r = this.doc.selection.createRange();
21250                     if(r){
21251                         var target = r.parentElement();
21252                         if(!target || target.tagName.toLowerCase() != 'li'){
21253                             e.stopEvent();
21254                             r.pasteHTML('<br />');
21255                             r.collapse(false);
21256                             r.select();
21257                         }
21258                     }
21259                 }
21260                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21261                     this.cleanUpPaste.defer(100, this);
21262                     return;
21263                 }
21264                 
21265                 
21266             };
21267         }else if(Roo.isOpera){
21268             return function(e){
21269                 var k = e.getKey();
21270                 if(k == e.TAB){
21271                     e.stopEvent();
21272                     this.win.focus();
21273                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21274                     this.deferFocus();
21275                 }
21276                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21277                     this.cleanUpPaste.defer(100, this);
21278                     return;
21279                 }
21280                 
21281             };
21282         }else if(Roo.isSafari){
21283             return function(e){
21284                 var k = e.getKey();
21285                 
21286                 if(k == e.TAB){
21287                     e.stopEvent();
21288                     this.execCmd('InsertText','\t');
21289                     this.deferFocus();
21290                     return;
21291                 }
21292                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21293                     this.cleanUpPaste.defer(100, this);
21294                     return;
21295                 }
21296                 
21297              };
21298         }
21299     }(),
21300     
21301     getAllAncestors: function()
21302     {
21303         var p = this.getSelectedNode();
21304         var a = [];
21305         if (!p) {
21306             a.push(p); // push blank onto stack..
21307             p = this.getParentElement();
21308         }
21309         
21310         
21311         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21312             a.push(p);
21313             p = p.parentNode;
21314         }
21315         a.push(this.doc.body);
21316         return a;
21317     },
21318     lastSel : false,
21319     lastSelNode : false,
21320     
21321     
21322     getSelection : function() 
21323     {
21324         this.assignDocWin();
21325         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21326     },
21327     
21328     getSelectedNode: function() 
21329     {
21330         // this may only work on Gecko!!!
21331         
21332         // should we cache this!!!!
21333         
21334         
21335         
21336          
21337         var range = this.createRange(this.getSelection()).cloneRange();
21338         
21339         if (Roo.isIE) {
21340             var parent = range.parentElement();
21341             while (true) {
21342                 var testRange = range.duplicate();
21343                 testRange.moveToElementText(parent);
21344                 if (testRange.inRange(range)) {
21345                     break;
21346                 }
21347                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21348                     break;
21349                 }
21350                 parent = parent.parentElement;
21351             }
21352             return parent;
21353         }
21354         
21355         // is ancestor a text element.
21356         var ac =  range.commonAncestorContainer;
21357         if (ac.nodeType == 3) {
21358             ac = ac.parentNode;
21359         }
21360         
21361         var ar = ac.childNodes;
21362          
21363         var nodes = [];
21364         var other_nodes = [];
21365         var has_other_nodes = false;
21366         for (var i=0;i<ar.length;i++) {
21367             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21368                 continue;
21369             }
21370             // fullly contained node.
21371             
21372             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21373                 nodes.push(ar[i]);
21374                 continue;
21375             }
21376             
21377             // probably selected..
21378             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21379                 other_nodes.push(ar[i]);
21380                 continue;
21381             }
21382             // outer..
21383             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21384                 continue;
21385             }
21386             
21387             
21388             has_other_nodes = true;
21389         }
21390         if (!nodes.length && other_nodes.length) {
21391             nodes= other_nodes;
21392         }
21393         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21394             return false;
21395         }
21396         
21397         return nodes[0];
21398     },
21399     createRange: function(sel)
21400     {
21401         // this has strange effects when using with 
21402         // top toolbar - not sure if it's a great idea.
21403         //this.editor.contentWindow.focus();
21404         if (typeof sel != "undefined") {
21405             try {
21406                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21407             } catch(e) {
21408                 return this.doc.createRange();
21409             }
21410         } else {
21411             return this.doc.createRange();
21412         }
21413     },
21414     getParentElement: function()
21415     {
21416         
21417         this.assignDocWin();
21418         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21419         
21420         var range = this.createRange(sel);
21421          
21422         try {
21423             var p = range.commonAncestorContainer;
21424             while (p.nodeType == 3) { // text node
21425                 p = p.parentNode;
21426             }
21427             return p;
21428         } catch (e) {
21429             return null;
21430         }
21431     
21432     },
21433     /***
21434      *
21435      * Range intersection.. the hard stuff...
21436      *  '-1' = before
21437      *  '0' = hits..
21438      *  '1' = after.
21439      *         [ -- selected range --- ]
21440      *   [fail]                        [fail]
21441      *
21442      *    basically..
21443      *      if end is before start or  hits it. fail.
21444      *      if start is after end or hits it fail.
21445      *
21446      *   if either hits (but other is outside. - then it's not 
21447      *   
21448      *    
21449      **/
21450     
21451     
21452     // @see http://www.thismuchiknow.co.uk/?p=64.
21453     rangeIntersectsNode : function(range, node)
21454     {
21455         var nodeRange = node.ownerDocument.createRange();
21456         try {
21457             nodeRange.selectNode(node);
21458         } catch (e) {
21459             nodeRange.selectNodeContents(node);
21460         }
21461     
21462         var rangeStartRange = range.cloneRange();
21463         rangeStartRange.collapse(true);
21464     
21465         var rangeEndRange = range.cloneRange();
21466         rangeEndRange.collapse(false);
21467     
21468         var nodeStartRange = nodeRange.cloneRange();
21469         nodeStartRange.collapse(true);
21470     
21471         var nodeEndRange = nodeRange.cloneRange();
21472         nodeEndRange.collapse(false);
21473     
21474         return rangeStartRange.compareBoundaryPoints(
21475                  Range.START_TO_START, nodeEndRange) == -1 &&
21476                rangeEndRange.compareBoundaryPoints(
21477                  Range.START_TO_START, nodeStartRange) == 1;
21478         
21479          
21480     },
21481     rangeCompareNode : function(range, node)
21482     {
21483         var nodeRange = node.ownerDocument.createRange();
21484         try {
21485             nodeRange.selectNode(node);
21486         } catch (e) {
21487             nodeRange.selectNodeContents(node);
21488         }
21489         
21490         
21491         range.collapse(true);
21492     
21493         nodeRange.collapse(true);
21494      
21495         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21496         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21497          
21498         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21499         
21500         var nodeIsBefore   =  ss == 1;
21501         var nodeIsAfter    = ee == -1;
21502         
21503         if (nodeIsBefore && nodeIsAfter) {
21504             return 0; // outer
21505         }
21506         if (!nodeIsBefore && nodeIsAfter) {
21507             return 1; //right trailed.
21508         }
21509         
21510         if (nodeIsBefore && !nodeIsAfter) {
21511             return 2;  // left trailed.
21512         }
21513         // fully contined.
21514         return 3;
21515     },
21516
21517     // private? - in a new class?
21518     cleanUpPaste :  function()
21519     {
21520         // cleans up the whole document..
21521         Roo.log('cleanuppaste');
21522         
21523         this.cleanUpChildren(this.doc.body);
21524         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21525         if (clean != this.doc.body.innerHTML) {
21526             this.doc.body.innerHTML = clean;
21527         }
21528         
21529     },
21530     
21531     cleanWordChars : function(input) {// change the chars to hex code
21532         var he = Roo.HtmlEditorCore;
21533         
21534         var output = input;
21535         Roo.each(he.swapCodes, function(sw) { 
21536             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21537             
21538             output = output.replace(swapper, sw[1]);
21539         });
21540         
21541         return output;
21542     },
21543     
21544     
21545     cleanUpChildren : function (n)
21546     {
21547         if (!n.childNodes.length) {
21548             return;
21549         }
21550         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21551            this.cleanUpChild(n.childNodes[i]);
21552         }
21553     },
21554     
21555     
21556         
21557     
21558     cleanUpChild : function (node)
21559     {
21560         var ed = this;
21561         //console.log(node);
21562         if (node.nodeName == "#text") {
21563             // clean up silly Windows -- stuff?
21564             return; 
21565         }
21566         if (node.nodeName == "#comment") {
21567             if (!this.allowComments) {
21568                 node.parentNode.removeChild(node);
21569             }
21570             // clean up silly Windows -- stuff?
21571             return; 
21572         }
21573         var lcname = node.tagName.toLowerCase();
21574         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21575         // whitelist of tags..
21576         
21577         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21578             // remove node.
21579             node.parentNode.removeChild(node);
21580             return;
21581             
21582         }
21583         
21584         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21585         
21586         // spans with no attributes - just remove them..
21587         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
21588             remove_keep_children = true;
21589         }
21590         
21591         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21592         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21593         
21594         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21595         //    remove_keep_children = true;
21596         //}
21597         
21598         if (remove_keep_children) {
21599             this.cleanUpChildren(node);
21600             // inserts everything just before this node...
21601             while (node.childNodes.length) {
21602                 var cn = node.childNodes[0];
21603                 node.removeChild(cn);
21604                 node.parentNode.insertBefore(cn, node);
21605             }
21606             node.parentNode.removeChild(node);
21607             return;
21608         }
21609         
21610         if (!node.attributes || !node.attributes.length) {
21611             
21612           
21613             
21614             
21615             this.cleanUpChildren(node);
21616             return;
21617         }
21618         
21619         function cleanAttr(n,v)
21620         {
21621             
21622             if (v.match(/^\./) || v.match(/^\//)) {
21623                 return;
21624             }
21625             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21626                 return;
21627             }
21628             if (v.match(/^#/)) {
21629                 return;
21630             }
21631             if (v.match(/^\{/)) { // allow template editing.
21632                 return;
21633             }
21634 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21635             node.removeAttribute(n);
21636             
21637         }
21638         
21639         var cwhite = this.cwhite;
21640         var cblack = this.cblack;
21641             
21642         function cleanStyle(n,v)
21643         {
21644             if (v.match(/expression/)) { //XSS?? should we even bother..
21645                 node.removeAttribute(n);
21646                 return;
21647             }
21648             
21649             var parts = v.split(/;/);
21650             var clean = [];
21651             
21652             Roo.each(parts, function(p) {
21653                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21654                 if (!p.length) {
21655                     return true;
21656                 }
21657                 var l = p.split(':').shift().replace(/\s+/g,'');
21658                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21659                 
21660                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21661 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21662                     //node.removeAttribute(n);
21663                     return true;
21664                 }
21665                 //Roo.log()
21666                 // only allow 'c whitelisted system attributes'
21667                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21668 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21669                     //node.removeAttribute(n);
21670                     return true;
21671                 }
21672                 
21673                 
21674                  
21675                 
21676                 clean.push(p);
21677                 return true;
21678             });
21679             if (clean.length) { 
21680                 node.setAttribute(n, clean.join(';'));
21681             } else {
21682                 node.removeAttribute(n);
21683             }
21684             
21685         }
21686         
21687         
21688         for (var i = node.attributes.length-1; i > -1 ; i--) {
21689             var a = node.attributes[i];
21690             //console.log(a);
21691             
21692             if (a.name.toLowerCase().substr(0,2)=='on')  {
21693                 node.removeAttribute(a.name);
21694                 continue;
21695             }
21696             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21697                 node.removeAttribute(a.name);
21698                 continue;
21699             }
21700             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21701                 cleanAttr(a.name,a.value); // fixme..
21702                 continue;
21703             }
21704             if (a.name == 'style') {
21705                 cleanStyle(a.name,a.value);
21706                 continue;
21707             }
21708             /// clean up MS crap..
21709             // tecnically this should be a list of valid class'es..
21710             
21711             
21712             if (a.name == 'class') {
21713                 if (a.value.match(/^Mso/)) {
21714                     node.removeAttribute('class');
21715                 }
21716                 
21717                 if (a.value.match(/^body$/)) {
21718                     node.removeAttribute('class');
21719                 }
21720                 continue;
21721             }
21722             
21723             // style cleanup!?
21724             // class cleanup?
21725             
21726         }
21727         
21728         
21729         this.cleanUpChildren(node);
21730         
21731         
21732     },
21733     
21734     /**
21735      * Clean up MS wordisms...
21736      */
21737     cleanWord : function(node)
21738     {
21739         if (!node) {
21740             this.cleanWord(this.doc.body);
21741             return;
21742         }
21743         
21744         if(
21745                 node.nodeName == 'SPAN' &&
21746                 !node.hasAttributes() &&
21747                 node.childNodes.length == 1 &&
21748                 node.firstChild.nodeName == "#text"  
21749         ) {
21750             var textNode = node.firstChild;
21751             node.removeChild(textNode);
21752             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21753                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
21754             }
21755             node.parentNode.insertBefore(textNode, node);
21756             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21757                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
21758             }
21759             node.parentNode.removeChild(node);
21760         }
21761         
21762         if (node.nodeName == "#text") {
21763             // clean up silly Windows -- stuff?
21764             return; 
21765         }
21766         if (node.nodeName == "#comment") {
21767             node.parentNode.removeChild(node);
21768             // clean up silly Windows -- stuff?
21769             return; 
21770         }
21771         
21772         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21773             node.parentNode.removeChild(node);
21774             return;
21775         }
21776         //Roo.log(node.tagName);
21777         // remove - but keep children..
21778         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
21779             //Roo.log('-- removed');
21780             while (node.childNodes.length) {
21781                 var cn = node.childNodes[0];
21782                 node.removeChild(cn);
21783                 node.parentNode.insertBefore(cn, node);
21784                 // move node to parent - and clean it..
21785                 this.cleanWord(cn);
21786             }
21787             node.parentNode.removeChild(node);
21788             /// no need to iterate chidlren = it's got none..
21789             //this.iterateChildren(node, this.cleanWord);
21790             return;
21791         }
21792         // clean styles
21793         if (node.className.length) {
21794             
21795             var cn = node.className.split(/\W+/);
21796             var cna = [];
21797             Roo.each(cn, function(cls) {
21798                 if (cls.match(/Mso[a-zA-Z]+/)) {
21799                     return;
21800                 }
21801                 cna.push(cls);
21802             });
21803             node.className = cna.length ? cna.join(' ') : '';
21804             if (!cna.length) {
21805                 node.removeAttribute("class");
21806             }
21807         }
21808         
21809         if (node.hasAttribute("lang")) {
21810             node.removeAttribute("lang");
21811         }
21812         
21813         if (node.hasAttribute("style")) {
21814             
21815             var styles = node.getAttribute("style").split(";");
21816             var nstyle = [];
21817             Roo.each(styles, function(s) {
21818                 if (!s.match(/:/)) {
21819                     return;
21820                 }
21821                 var kv = s.split(":");
21822                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21823                     return;
21824                 }
21825                 // what ever is left... we allow.
21826                 nstyle.push(s);
21827             });
21828             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21829             if (!nstyle.length) {
21830                 node.removeAttribute('style');
21831             }
21832         }
21833         this.iterateChildren(node, this.cleanWord);
21834         
21835         
21836         
21837     },
21838     /**
21839      * iterateChildren of a Node, calling fn each time, using this as the scole..
21840      * @param {DomNode} node node to iterate children of.
21841      * @param {Function} fn method of this class to call on each item.
21842      */
21843     iterateChildren : function(node, fn)
21844     {
21845         if (!node.childNodes.length) {
21846                 return;
21847         }
21848         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21849            fn.call(this, node.childNodes[i])
21850         }
21851     },
21852     
21853     
21854     /**
21855      * cleanTableWidths.
21856      *
21857      * Quite often pasting from word etc.. results in tables with column and widths.
21858      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21859      *
21860      */
21861     cleanTableWidths : function(node)
21862     {
21863          
21864          
21865         if (!node) {
21866             this.cleanTableWidths(this.doc.body);
21867             return;
21868         }
21869         
21870         // ignore list...
21871         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21872             return; 
21873         }
21874         Roo.log(node.tagName);
21875         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21876             this.iterateChildren(node, this.cleanTableWidths);
21877             return;
21878         }
21879         if (node.hasAttribute('width')) {
21880             node.removeAttribute('width');
21881         }
21882         
21883          
21884         if (node.hasAttribute("style")) {
21885             // pretty basic...
21886             
21887             var styles = node.getAttribute("style").split(";");
21888             var nstyle = [];
21889             Roo.each(styles, function(s) {
21890                 if (!s.match(/:/)) {
21891                     return;
21892                 }
21893                 var kv = s.split(":");
21894                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21895                     return;
21896                 }
21897                 // what ever is left... we allow.
21898                 nstyle.push(s);
21899             });
21900             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21901             if (!nstyle.length) {
21902                 node.removeAttribute('style');
21903             }
21904         }
21905         
21906         this.iterateChildren(node, this.cleanTableWidths);
21907         
21908         
21909     },
21910     
21911     
21912     
21913     
21914     domToHTML : function(currentElement, depth, nopadtext) {
21915         
21916         depth = depth || 0;
21917         nopadtext = nopadtext || false;
21918     
21919         if (!currentElement) {
21920             return this.domToHTML(this.doc.body);
21921         }
21922         
21923         //Roo.log(currentElement);
21924         var j;
21925         var allText = false;
21926         var nodeName = currentElement.nodeName;
21927         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21928         
21929         if  (nodeName == '#text') {
21930             
21931             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21932         }
21933         
21934         
21935         var ret = '';
21936         if (nodeName != 'BODY') {
21937              
21938             var i = 0;
21939             // Prints the node tagName, such as <A>, <IMG>, etc
21940             if (tagName) {
21941                 var attr = [];
21942                 for(i = 0; i < currentElement.attributes.length;i++) {
21943                     // quoting?
21944                     var aname = currentElement.attributes.item(i).name;
21945                     if (!currentElement.attributes.item(i).value.length) {
21946                         continue;
21947                     }
21948                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21949                 }
21950                 
21951                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21952             } 
21953             else {
21954                 
21955                 // eack
21956             }
21957         } else {
21958             tagName = false;
21959         }
21960         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21961             return ret;
21962         }
21963         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21964             nopadtext = true;
21965         }
21966         
21967         
21968         // Traverse the tree
21969         i = 0;
21970         var currentElementChild = currentElement.childNodes.item(i);
21971         var allText = true;
21972         var innerHTML  = '';
21973         lastnode = '';
21974         while (currentElementChild) {
21975             // Formatting code (indent the tree so it looks nice on the screen)
21976             var nopad = nopadtext;
21977             if (lastnode == 'SPAN') {
21978                 nopad  = true;
21979             }
21980             // text
21981             if  (currentElementChild.nodeName == '#text') {
21982                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21983                 toadd = nopadtext ? toadd : toadd.trim();
21984                 if (!nopad && toadd.length > 80) {
21985                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21986                 }
21987                 innerHTML  += toadd;
21988                 
21989                 i++;
21990                 currentElementChild = currentElement.childNodes.item(i);
21991                 lastNode = '';
21992                 continue;
21993             }
21994             allText = false;
21995             
21996             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21997                 
21998             // Recursively traverse the tree structure of the child node
21999             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22000             lastnode = currentElementChild.nodeName;
22001             i++;
22002             currentElementChild=currentElement.childNodes.item(i);
22003         }
22004         
22005         ret += innerHTML;
22006         
22007         if (!allText) {
22008                 // The remaining code is mostly for formatting the tree
22009             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22010         }
22011         
22012         
22013         if (tagName) {
22014             ret+= "</"+tagName+">";
22015         }
22016         return ret;
22017         
22018     },
22019         
22020     applyBlacklists : function()
22021     {
22022         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22023         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22024         
22025         this.white = [];
22026         this.black = [];
22027         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22028             if (b.indexOf(tag) > -1) {
22029                 return;
22030             }
22031             this.white.push(tag);
22032             
22033         }, this);
22034         
22035         Roo.each(w, function(tag) {
22036             if (b.indexOf(tag) > -1) {
22037                 return;
22038             }
22039             if (this.white.indexOf(tag) > -1) {
22040                 return;
22041             }
22042             this.white.push(tag);
22043             
22044         }, this);
22045         
22046         
22047         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22048             if (w.indexOf(tag) > -1) {
22049                 return;
22050             }
22051             this.black.push(tag);
22052             
22053         }, this);
22054         
22055         Roo.each(b, function(tag) {
22056             if (w.indexOf(tag) > -1) {
22057                 return;
22058             }
22059             if (this.black.indexOf(tag) > -1) {
22060                 return;
22061             }
22062             this.black.push(tag);
22063             
22064         }, this);
22065         
22066         
22067         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22068         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22069         
22070         this.cwhite = [];
22071         this.cblack = [];
22072         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22073             if (b.indexOf(tag) > -1) {
22074                 return;
22075             }
22076             this.cwhite.push(tag);
22077             
22078         }, this);
22079         
22080         Roo.each(w, function(tag) {
22081             if (b.indexOf(tag) > -1) {
22082                 return;
22083             }
22084             if (this.cwhite.indexOf(tag) > -1) {
22085                 return;
22086             }
22087             this.cwhite.push(tag);
22088             
22089         }, this);
22090         
22091         
22092         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22093             if (w.indexOf(tag) > -1) {
22094                 return;
22095             }
22096             this.cblack.push(tag);
22097             
22098         }, this);
22099         
22100         Roo.each(b, function(tag) {
22101             if (w.indexOf(tag) > -1) {
22102                 return;
22103             }
22104             if (this.cblack.indexOf(tag) > -1) {
22105                 return;
22106             }
22107             this.cblack.push(tag);
22108             
22109         }, this);
22110     },
22111     
22112     setStylesheets : function(stylesheets)
22113     {
22114         if(typeof(stylesheets) == 'string'){
22115             Roo.get(this.iframe.contentDocument.head).createChild({
22116                 tag : 'link',
22117                 rel : 'stylesheet',
22118                 type : 'text/css',
22119                 href : stylesheets
22120             });
22121             
22122             return;
22123         }
22124         var _this = this;
22125      
22126         Roo.each(stylesheets, function(s) {
22127             if(!s.length){
22128                 return;
22129             }
22130             
22131             Roo.get(_this.iframe.contentDocument.head).createChild({
22132                 tag : 'link',
22133                 rel : 'stylesheet',
22134                 type : 'text/css',
22135                 href : s
22136             });
22137         });
22138
22139         
22140     },
22141     
22142     removeStylesheets : function()
22143     {
22144         var _this = this;
22145         
22146         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22147             s.remove();
22148         });
22149     },
22150     
22151     setStyle : function(style)
22152     {
22153         Roo.get(this.iframe.contentDocument.head).createChild({
22154             tag : 'style',
22155             type : 'text/css',
22156             html : style
22157         });
22158
22159         return;
22160     }
22161     
22162     // hide stuff that is not compatible
22163     /**
22164      * @event blur
22165      * @hide
22166      */
22167     /**
22168      * @event change
22169      * @hide
22170      */
22171     /**
22172      * @event focus
22173      * @hide
22174      */
22175     /**
22176      * @event specialkey
22177      * @hide
22178      */
22179     /**
22180      * @cfg {String} fieldClass @hide
22181      */
22182     /**
22183      * @cfg {String} focusClass @hide
22184      */
22185     /**
22186      * @cfg {String} autoCreate @hide
22187      */
22188     /**
22189      * @cfg {String} inputType @hide
22190      */
22191     /**
22192      * @cfg {String} invalidClass @hide
22193      */
22194     /**
22195      * @cfg {String} invalidText @hide
22196      */
22197     /**
22198      * @cfg {String} msgFx @hide
22199      */
22200     /**
22201      * @cfg {String} validateOnBlur @hide
22202      */
22203 });
22204
22205 Roo.HtmlEditorCore.white = [
22206         'area', 'br', 'img', 'input', 'hr', 'wbr',
22207         
22208        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22209        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22210        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22211        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22212        'table',   'ul',         'xmp', 
22213        
22214        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22215       'thead',   'tr', 
22216      
22217       'dir', 'menu', 'ol', 'ul', 'dl',
22218        
22219       'embed',  'object'
22220 ];
22221
22222
22223 Roo.HtmlEditorCore.black = [
22224     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22225         'applet', // 
22226         'base',   'basefont', 'bgsound', 'blink',  'body', 
22227         'frame',  'frameset', 'head',    'html',   'ilayer', 
22228         'iframe', 'layer',  'link',     'meta',    'object',   
22229         'script', 'style' ,'title',  'xml' // clean later..
22230 ];
22231 Roo.HtmlEditorCore.clean = [
22232     'script', 'style', 'title', 'xml'
22233 ];
22234 Roo.HtmlEditorCore.remove = [
22235     'font'
22236 ];
22237 // attributes..
22238
22239 Roo.HtmlEditorCore.ablack = [
22240     'on'
22241 ];
22242     
22243 Roo.HtmlEditorCore.aclean = [ 
22244     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22245 ];
22246
22247 // protocols..
22248 Roo.HtmlEditorCore.pwhite= [
22249         'http',  'https',  'mailto'
22250 ];
22251
22252 // white listed style attributes.
22253 Roo.HtmlEditorCore.cwhite= [
22254       //  'text-align', /// default is to allow most things..
22255       
22256          
22257 //        'font-size'//??
22258 ];
22259
22260 // black listed style attributes.
22261 Roo.HtmlEditorCore.cblack= [
22262       //  'font-size' -- this can be set by the project 
22263 ];
22264
22265
22266 Roo.HtmlEditorCore.swapCodes   =[ 
22267     [    8211, "&#8211;" ], 
22268     [    8212, "&#8212;" ], 
22269     [    8216,  "'" ],  
22270     [    8217, "'" ],  
22271     [    8220, '"' ],  
22272     [    8221, '"' ],  
22273     [    8226, "*" ],  
22274     [    8230, "..." ]
22275 ]; 
22276
22277     //<script type="text/javascript">
22278
22279 /*
22280  * Ext JS Library 1.1.1
22281  * Copyright(c) 2006-2007, Ext JS, LLC.
22282  * Licence LGPL
22283  * 
22284  */
22285  
22286  
22287 Roo.form.HtmlEditor = function(config){
22288     
22289     
22290     
22291     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22292     
22293     if (!this.toolbars) {
22294         this.toolbars = [];
22295     }
22296     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22297     
22298     
22299 };
22300
22301 /**
22302  * @class Roo.form.HtmlEditor
22303  * @extends Roo.form.Field
22304  * Provides a lightweight HTML Editor component.
22305  *
22306  * This has been tested on Fireforx / Chrome.. IE may not be so great..
22307  * 
22308  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22309  * supported by this editor.</b><br/><br/>
22310  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22311  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22312  */
22313 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22314     /**
22315      * @cfg {Boolean} clearUp
22316      */
22317     clearUp : true,
22318       /**
22319      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22320      */
22321     toolbars : false,
22322    
22323      /**
22324      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22325      *                        Roo.resizable.
22326      */
22327     resizable : false,
22328      /**
22329      * @cfg {Number} height (in pixels)
22330      */   
22331     height: 300,
22332    /**
22333      * @cfg {Number} width (in pixels)
22334      */   
22335     width: 500,
22336     
22337     /**
22338      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22339      * 
22340      */
22341     stylesheets: false,
22342     
22343     
22344      /**
22345      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22346      * 
22347      */
22348     cblack: false,
22349     /**
22350      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22351      * 
22352      */
22353     cwhite: false,
22354     
22355      /**
22356      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22357      * 
22358      */
22359     black: false,
22360     /**
22361      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22362      * 
22363      */
22364     white: false,
22365     /**
22366      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
22367      */
22368     allowComments: false,
22369     
22370     // id of frame..
22371     frameId: false,
22372     
22373     // private properties
22374     validationEvent : false,
22375     deferHeight: true,
22376     initialized : false,
22377     activated : false,
22378     
22379     onFocus : Roo.emptyFn,
22380     iframePad:3,
22381     hideMode:'offsets',
22382     
22383     actionMode : 'container', // defaults to hiding it...
22384     
22385     defaultAutoCreate : { // modified by initCompnoent..
22386         tag: "textarea",
22387         style:"width:500px;height:300px;",
22388         autocomplete: "new-password"
22389     },
22390
22391     // private
22392     initComponent : function(){
22393         this.addEvents({
22394             /**
22395              * @event initialize
22396              * Fires when the editor is fully initialized (including the iframe)
22397              * @param {HtmlEditor} this
22398              */
22399             initialize: true,
22400             /**
22401              * @event activate
22402              * Fires when the editor is first receives the focus. Any insertion must wait
22403              * until after this event.
22404              * @param {HtmlEditor} this
22405              */
22406             activate: true,
22407              /**
22408              * @event beforesync
22409              * Fires before the textarea is updated with content from the editor iframe. Return false
22410              * to cancel the sync.
22411              * @param {HtmlEditor} this
22412              * @param {String} html
22413              */
22414             beforesync: true,
22415              /**
22416              * @event beforepush
22417              * Fires before the iframe editor is updated with content from the textarea. Return false
22418              * to cancel the push.
22419              * @param {HtmlEditor} this
22420              * @param {String} html
22421              */
22422             beforepush: true,
22423              /**
22424              * @event sync
22425              * Fires when the textarea is updated with content from the editor iframe.
22426              * @param {HtmlEditor} this
22427              * @param {String} html
22428              */
22429             sync: true,
22430              /**
22431              * @event push
22432              * Fires when the iframe editor is updated with content from the textarea.
22433              * @param {HtmlEditor} this
22434              * @param {String} html
22435              */
22436             push: true,
22437              /**
22438              * @event editmodechange
22439              * Fires when the editor switches edit modes
22440              * @param {HtmlEditor} this
22441              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22442              */
22443             editmodechange: true,
22444             /**
22445              * @event editorevent
22446              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22447              * @param {HtmlEditor} this
22448              */
22449             editorevent: true,
22450             /**
22451              * @event firstfocus
22452              * Fires when on first focus - needed by toolbars..
22453              * @param {HtmlEditor} this
22454              */
22455             firstfocus: true,
22456             /**
22457              * @event autosave
22458              * Auto save the htmlEditor value as a file into Events
22459              * @param {HtmlEditor} this
22460              */
22461             autosave: true,
22462             /**
22463              * @event savedpreview
22464              * preview the saved version of htmlEditor
22465              * @param {HtmlEditor} this
22466              */
22467             savedpreview: true,
22468             
22469             /**
22470             * @event stylesheetsclick
22471             * Fires when press the Sytlesheets button
22472             * @param {Roo.HtmlEditorCore} this
22473             */
22474             stylesheetsclick: true
22475         });
22476         this.defaultAutoCreate =  {
22477             tag: "textarea",
22478             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22479             autocomplete: "new-password"
22480         };
22481     },
22482
22483     /**
22484      * Protected method that will not generally be called directly. It
22485      * is called when the editor creates its toolbar. Override this method if you need to
22486      * add custom toolbar buttons.
22487      * @param {HtmlEditor} editor
22488      */
22489     createToolbar : function(editor){
22490         Roo.log("create toolbars");
22491         if (!editor.toolbars || !editor.toolbars.length) {
22492             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22493         }
22494         
22495         for (var i =0 ; i < editor.toolbars.length;i++) {
22496             editor.toolbars[i] = Roo.factory(
22497                     typeof(editor.toolbars[i]) == 'string' ?
22498                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
22499                 Roo.form.HtmlEditor);
22500             editor.toolbars[i].init(editor);
22501         }
22502          
22503         
22504     },
22505
22506      
22507     // private
22508     onRender : function(ct, position)
22509     {
22510         var _t = this;
22511         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22512         
22513         this.wrap = this.el.wrap({
22514             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22515         });
22516         
22517         this.editorcore.onRender(ct, position);
22518          
22519         if (this.resizable) {
22520             this.resizeEl = new Roo.Resizable(this.wrap, {
22521                 pinned : true,
22522                 wrap: true,
22523                 dynamic : true,
22524                 minHeight : this.height,
22525                 height: this.height,
22526                 handles : this.resizable,
22527                 width: this.width,
22528                 listeners : {
22529                     resize : function(r, w, h) {
22530                         _t.onResize(w,h); // -something
22531                     }
22532                 }
22533             });
22534             
22535         }
22536         this.createToolbar(this);
22537        
22538         
22539         if(!this.width){
22540             this.setSize(this.wrap.getSize());
22541         }
22542         if (this.resizeEl) {
22543             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22544             // should trigger onReize..
22545         }
22546         
22547         this.keyNav = new Roo.KeyNav(this.el, {
22548             
22549             "tab" : function(e){
22550                 e.preventDefault();
22551                 
22552                 var value = this.getValue();
22553                 
22554                 var start = this.el.dom.selectionStart;
22555                 var end = this.el.dom.selectionEnd;
22556                 
22557                 if(!e.shiftKey){
22558                     
22559                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22560                     this.el.dom.setSelectionRange(end + 1, end + 1);
22561                     return;
22562                 }
22563                 
22564                 var f = value.substring(0, start).split("\t");
22565                 
22566                 if(f.pop().length != 0){
22567                     return;
22568                 }
22569                 
22570                 this.setValue(f.join("\t") + value.substring(end));
22571                 this.el.dom.setSelectionRange(start - 1, start - 1);
22572                 
22573             },
22574             
22575             "home" : function(e){
22576                 e.preventDefault();
22577                 
22578                 var curr = this.el.dom.selectionStart;
22579                 var lines = this.getValue().split("\n");
22580                 
22581                 if(!lines.length){
22582                     return;
22583                 }
22584                 
22585                 if(e.ctrlKey){
22586                     this.el.dom.setSelectionRange(0, 0);
22587                     return;
22588                 }
22589                 
22590                 var pos = 0;
22591                 
22592                 for (var i = 0; i < lines.length;i++) {
22593                     pos += lines[i].length;
22594                     
22595                     if(i != 0){
22596                         pos += 1;
22597                     }
22598                     
22599                     if(pos < curr){
22600                         continue;
22601                     }
22602                     
22603                     pos -= lines[i].length;
22604                     
22605                     break;
22606                 }
22607                 
22608                 if(!e.shiftKey){
22609                     this.el.dom.setSelectionRange(pos, pos);
22610                     return;
22611                 }
22612                 
22613                 this.el.dom.selectionStart = pos;
22614                 this.el.dom.selectionEnd = curr;
22615             },
22616             
22617             "end" : function(e){
22618                 e.preventDefault();
22619                 
22620                 var curr = this.el.dom.selectionStart;
22621                 var lines = this.getValue().split("\n");
22622                 
22623                 if(!lines.length){
22624                     return;
22625                 }
22626                 
22627                 if(e.ctrlKey){
22628                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22629                     return;
22630                 }
22631                 
22632                 var pos = 0;
22633                 
22634                 for (var i = 0; i < lines.length;i++) {
22635                     
22636                     pos += lines[i].length;
22637                     
22638                     if(i != 0){
22639                         pos += 1;
22640                     }
22641                     
22642                     if(pos < curr){
22643                         continue;
22644                     }
22645                     
22646                     break;
22647                 }
22648                 
22649                 if(!e.shiftKey){
22650                     this.el.dom.setSelectionRange(pos, pos);
22651                     return;
22652                 }
22653                 
22654                 this.el.dom.selectionStart = curr;
22655                 this.el.dom.selectionEnd = pos;
22656             },
22657
22658             scope : this,
22659
22660             doRelay : function(foo, bar, hname){
22661                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22662             },
22663
22664             forceKeyDown: true
22665         });
22666         
22667 //        if(this.autosave && this.w){
22668 //            this.autoSaveFn = setInterval(this.autosave, 1000);
22669 //        }
22670     },
22671
22672     // private
22673     onResize : function(w, h)
22674     {
22675         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22676         var ew = false;
22677         var eh = false;
22678         
22679         if(this.el ){
22680             if(typeof w == 'number'){
22681                 var aw = w - this.wrap.getFrameWidth('lr');
22682                 this.el.setWidth(this.adjustWidth('textarea', aw));
22683                 ew = aw;
22684             }
22685             if(typeof h == 'number'){
22686                 var tbh = 0;
22687                 for (var i =0; i < this.toolbars.length;i++) {
22688                     // fixme - ask toolbars for heights?
22689                     tbh += this.toolbars[i].tb.el.getHeight();
22690                     if (this.toolbars[i].footer) {
22691                         tbh += this.toolbars[i].footer.el.getHeight();
22692                     }
22693                 }
22694                 
22695                 
22696                 
22697                 
22698                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22699                 ah -= 5; // knock a few pixes off for look..
22700 //                Roo.log(ah);
22701                 this.el.setHeight(this.adjustWidth('textarea', ah));
22702                 var eh = ah;
22703             }
22704         }
22705         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22706         this.editorcore.onResize(ew,eh);
22707         
22708     },
22709
22710     /**
22711      * Toggles the editor between standard and source edit mode.
22712      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22713      */
22714     toggleSourceEdit : function(sourceEditMode)
22715     {
22716         this.editorcore.toggleSourceEdit(sourceEditMode);
22717         
22718         if(this.editorcore.sourceEditMode){
22719             Roo.log('editor - showing textarea');
22720             
22721 //            Roo.log('in');
22722 //            Roo.log(this.syncValue());
22723             this.editorcore.syncValue();
22724             this.el.removeClass('x-hidden');
22725             this.el.dom.removeAttribute('tabIndex');
22726             this.el.focus();
22727             
22728             for (var i = 0; i < this.toolbars.length; i++) {
22729                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22730                     this.toolbars[i].tb.hide();
22731                     this.toolbars[i].footer.hide();
22732                 }
22733             }
22734             
22735         }else{
22736             Roo.log('editor - hiding textarea');
22737 //            Roo.log('out')
22738 //            Roo.log(this.pushValue()); 
22739             this.editorcore.pushValue();
22740             
22741             this.el.addClass('x-hidden');
22742             this.el.dom.setAttribute('tabIndex', -1);
22743             
22744             for (var i = 0; i < this.toolbars.length; i++) {
22745                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22746                     this.toolbars[i].tb.show();
22747                     this.toolbars[i].footer.show();
22748                 }
22749             }
22750             
22751             //this.deferFocus();
22752         }
22753         
22754         this.setSize(this.wrap.getSize());
22755         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
22756         
22757         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22758     },
22759  
22760     // private (for BoxComponent)
22761     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22762
22763     // private (for BoxComponent)
22764     getResizeEl : function(){
22765         return this.wrap;
22766     },
22767
22768     // private (for BoxComponent)
22769     getPositionEl : function(){
22770         return this.wrap;
22771     },
22772
22773     // private
22774     initEvents : function(){
22775         this.originalValue = this.getValue();
22776     },
22777
22778     /**
22779      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22780      * @method
22781      */
22782     markInvalid : Roo.emptyFn,
22783     /**
22784      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22785      * @method
22786      */
22787     clearInvalid : Roo.emptyFn,
22788
22789     setValue : function(v){
22790         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
22791         this.editorcore.pushValue();
22792     },
22793
22794      
22795     // private
22796     deferFocus : function(){
22797         this.focus.defer(10, this);
22798     },
22799
22800     // doc'ed in Field
22801     focus : function(){
22802         this.editorcore.focus();
22803         
22804     },
22805       
22806
22807     // private
22808     onDestroy : function(){
22809         
22810         
22811         
22812         if(this.rendered){
22813             
22814             for (var i =0; i < this.toolbars.length;i++) {
22815                 // fixme - ask toolbars for heights?
22816                 this.toolbars[i].onDestroy();
22817             }
22818             
22819             this.wrap.dom.innerHTML = '';
22820             this.wrap.remove();
22821         }
22822     },
22823
22824     // private
22825     onFirstFocus : function(){
22826         //Roo.log("onFirstFocus");
22827         this.editorcore.onFirstFocus();
22828          for (var i =0; i < this.toolbars.length;i++) {
22829             this.toolbars[i].onFirstFocus();
22830         }
22831         
22832     },
22833     
22834     // private
22835     syncValue : function()
22836     {
22837         this.editorcore.syncValue();
22838     },
22839     
22840     pushValue : function()
22841     {
22842         this.editorcore.pushValue();
22843     },
22844     
22845     setStylesheets : function(stylesheets)
22846     {
22847         this.editorcore.setStylesheets(stylesheets);
22848     },
22849     
22850     removeStylesheets : function()
22851     {
22852         this.editorcore.removeStylesheets();
22853     }
22854      
22855     
22856     // hide stuff that is not compatible
22857     /**
22858      * @event blur
22859      * @hide
22860      */
22861     /**
22862      * @event change
22863      * @hide
22864      */
22865     /**
22866      * @event focus
22867      * @hide
22868      */
22869     /**
22870      * @event specialkey
22871      * @hide
22872      */
22873     /**
22874      * @cfg {String} fieldClass @hide
22875      */
22876     /**
22877      * @cfg {String} focusClass @hide
22878      */
22879     /**
22880      * @cfg {String} autoCreate @hide
22881      */
22882     /**
22883      * @cfg {String} inputType @hide
22884      */
22885     /**
22886      * @cfg {String} invalidClass @hide
22887      */
22888     /**
22889      * @cfg {String} invalidText @hide
22890      */
22891     /**
22892      * @cfg {String} msgFx @hide
22893      */
22894     /**
22895      * @cfg {String} validateOnBlur @hide
22896      */
22897 });
22898  
22899     // <script type="text/javascript">
22900 /*
22901  * Based on
22902  * Ext JS Library 1.1.1
22903  * Copyright(c) 2006-2007, Ext JS, LLC.
22904  *  
22905  
22906  */
22907
22908 /**
22909  * @class Roo.form.HtmlEditorToolbar1
22910  * Basic Toolbar
22911  * 
22912  * Usage:
22913  *
22914  new Roo.form.HtmlEditor({
22915     ....
22916     toolbars : [
22917         new Roo.form.HtmlEditorToolbar1({
22918             disable : { fonts: 1 , format: 1, ..., ... , ...],
22919             btns : [ .... ]
22920         })
22921     }
22922      
22923  * 
22924  * @cfg {Object} disable List of elements to disable..
22925  * @cfg {Array} btns List of additional buttons.
22926  * 
22927  * 
22928  * NEEDS Extra CSS? 
22929  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22930  */
22931  
22932 Roo.form.HtmlEditor.ToolbarStandard = function(config)
22933 {
22934     
22935     Roo.apply(this, config);
22936     
22937     // default disabled, based on 'good practice'..
22938     this.disable = this.disable || {};
22939     Roo.applyIf(this.disable, {
22940         fontSize : true,
22941         colors : true,
22942         specialElements : true
22943     });
22944     
22945     
22946     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22947     // dont call parent... till later.
22948 }
22949
22950 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
22951     
22952     tb: false,
22953     
22954     rendered: false,
22955     
22956     editor : false,
22957     editorcore : false,
22958     /**
22959      * @cfg {Object} disable  List of toolbar elements to disable
22960          
22961      */
22962     disable : false,
22963     
22964     
22965      /**
22966      * @cfg {String} createLinkText The default text for the create link prompt
22967      */
22968     createLinkText : 'Please enter the URL for the link:',
22969     /**
22970      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
22971      */
22972     defaultLinkValue : 'http:/'+'/',
22973    
22974     
22975       /**
22976      * @cfg {Array} fontFamilies An array of available font families
22977      */
22978     fontFamilies : [
22979         'Arial',
22980         'Courier New',
22981         'Tahoma',
22982         'Times New Roman',
22983         'Verdana'
22984     ],
22985     
22986     specialChars : [
22987            "&#169;",
22988           "&#174;",     
22989           "&#8482;",    
22990           "&#163;" ,    
22991          // "&#8212;",    
22992           "&#8230;",    
22993           "&#247;" ,    
22994         //  "&#225;" ,     ?? a acute?
22995            "&#8364;"    , //Euro
22996        //   "&#8220;"    ,
22997         //  "&#8221;"    ,
22998         //  "&#8226;"    ,
22999           "&#176;"  //   , // degrees
23000
23001          // "&#233;"     , // e ecute
23002          // "&#250;"     , // u ecute?
23003     ],
23004     
23005     specialElements : [
23006         {
23007             text: "Insert Table",
23008             xtype: 'MenuItem',
23009             xns : Roo.Menu,
23010             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
23011                 
23012         },
23013         {    
23014             text: "Insert Image",
23015             xtype: 'MenuItem',
23016             xns : Roo.Menu,
23017             ihtml : '<img src="about:blank"/>'
23018             
23019         }
23020         
23021          
23022     ],
23023     
23024     
23025     inputElements : [ 
23026             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
23027             "input:submit", "input:button", "select", "textarea", "label" ],
23028     formats : [
23029         ["p"] ,  
23030         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
23031         ["pre"],[ "code"], 
23032         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
23033         ['div'],['span'],
23034         ['sup'],['sub']
23035     ],
23036     
23037     cleanStyles : [
23038         "font-size"
23039     ],
23040      /**
23041      * @cfg {String} defaultFont default font to use.
23042      */
23043     defaultFont: 'tahoma',
23044    
23045     fontSelect : false,
23046     
23047     
23048     formatCombo : false,
23049     
23050     init : function(editor)
23051     {
23052         this.editor = editor;
23053         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23054         var editorcore = this.editorcore;
23055         
23056         var _t = this;
23057         
23058         var fid = editorcore.frameId;
23059         var etb = this;
23060         function btn(id, toggle, handler){
23061             var xid = fid + '-'+ id ;
23062             return {
23063                 id : xid,
23064                 cmd : id,
23065                 cls : 'x-btn-icon x-edit-'+id,
23066                 enableToggle:toggle !== false,
23067                 scope: _t, // was editor...
23068                 handler:handler||_t.relayBtnCmd,
23069                 clickEvent:'mousedown',
23070                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23071                 tabIndex:-1
23072             };
23073         }
23074         
23075         
23076         
23077         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
23078         this.tb = tb;
23079          // stop form submits
23080         tb.el.on('click', function(e){
23081             e.preventDefault(); // what does this do?
23082         });
23083
23084         if(!this.disable.font) { // && !Roo.isSafari){
23085             /* why no safari for fonts 
23086             editor.fontSelect = tb.el.createChild({
23087                 tag:'select',
23088                 tabIndex: -1,
23089                 cls:'x-font-select',
23090                 html: this.createFontOptions()
23091             });
23092             
23093             editor.fontSelect.on('change', function(){
23094                 var font = editor.fontSelect.dom.value;
23095                 editor.relayCmd('fontname', font);
23096                 editor.deferFocus();
23097             }, editor);
23098             
23099             tb.add(
23100                 editor.fontSelect.dom,
23101                 '-'
23102             );
23103             */
23104             
23105         };
23106         if(!this.disable.formats){
23107             this.formatCombo = new Roo.form.ComboBox({
23108                 store: new Roo.data.SimpleStore({
23109                     id : 'tag',
23110                     fields: ['tag'],
23111                     data : this.formats // from states.js
23112                 }),
23113                 blockFocus : true,
23114                 name : '',
23115                 //autoCreate : {tag: "div",  size: "20"},
23116                 displayField:'tag',
23117                 typeAhead: false,
23118                 mode: 'local',
23119                 editable : false,
23120                 triggerAction: 'all',
23121                 emptyText:'Add tag',
23122                 selectOnFocus:true,
23123                 width:135,
23124                 listeners : {
23125                     'select': function(c, r, i) {
23126                         editorcore.insertTag(r.get('tag'));
23127                         editor.focus();
23128                     }
23129                 }
23130
23131             });
23132             tb.addField(this.formatCombo);
23133             
23134         }
23135         
23136         if(!this.disable.format){
23137             tb.add(
23138                 btn('bold'),
23139                 btn('italic'),
23140                 btn('underline'),
23141                 btn('strikethrough')
23142             );
23143         };
23144         if(!this.disable.fontSize){
23145             tb.add(
23146                 '-',
23147                 
23148                 
23149                 btn('increasefontsize', false, editorcore.adjustFont),
23150                 btn('decreasefontsize', false, editorcore.adjustFont)
23151             );
23152         };
23153         
23154         
23155         if(!this.disable.colors){
23156             tb.add(
23157                 '-', {
23158                     id:editorcore.frameId +'-forecolor',
23159                     cls:'x-btn-icon x-edit-forecolor',
23160                     clickEvent:'mousedown',
23161                     tooltip: this.buttonTips['forecolor'] || undefined,
23162                     tabIndex:-1,
23163                     menu : new Roo.menu.ColorMenu({
23164                         allowReselect: true,
23165                         focus: Roo.emptyFn,
23166                         value:'000000',
23167                         plain:true,
23168                         selectHandler: function(cp, color){
23169                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
23170                             editor.deferFocus();
23171                         },
23172                         scope: editorcore,
23173                         clickEvent:'mousedown'
23174                     })
23175                 }, {
23176                     id:editorcore.frameId +'backcolor',
23177                     cls:'x-btn-icon x-edit-backcolor',
23178                     clickEvent:'mousedown',
23179                     tooltip: this.buttonTips['backcolor'] || undefined,
23180                     tabIndex:-1,
23181                     menu : new Roo.menu.ColorMenu({
23182                         focus: Roo.emptyFn,
23183                         value:'FFFFFF',
23184                         plain:true,
23185                         allowReselect: true,
23186                         selectHandler: function(cp, color){
23187                             if(Roo.isGecko){
23188                                 editorcore.execCmd('useCSS', false);
23189                                 editorcore.execCmd('hilitecolor', color);
23190                                 editorcore.execCmd('useCSS', true);
23191                                 editor.deferFocus();
23192                             }else{
23193                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
23194                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
23195                                 editor.deferFocus();
23196                             }
23197                         },
23198                         scope:editorcore,
23199                         clickEvent:'mousedown'
23200                     })
23201                 }
23202             );
23203         };
23204         // now add all the items...
23205         
23206
23207         if(!this.disable.alignments){
23208             tb.add(
23209                 '-',
23210                 btn('justifyleft'),
23211                 btn('justifycenter'),
23212                 btn('justifyright')
23213             );
23214         };
23215
23216         //if(!Roo.isSafari){
23217             if(!this.disable.links){
23218                 tb.add(
23219                     '-',
23220                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
23221                 );
23222             };
23223
23224             if(!this.disable.lists){
23225                 tb.add(
23226                     '-',
23227                     btn('insertorderedlist'),
23228                     btn('insertunorderedlist')
23229                 );
23230             }
23231             if(!this.disable.sourceEdit){
23232                 tb.add(
23233                     '-',
23234                     btn('sourceedit', true, function(btn){
23235                         this.toggleSourceEdit(btn.pressed);
23236                     })
23237                 );
23238             }
23239         //}
23240         
23241         var smenu = { };
23242         // special menu.. - needs to be tidied up..
23243         if (!this.disable.special) {
23244             smenu = {
23245                 text: "&#169;",
23246                 cls: 'x-edit-none',
23247                 
23248                 menu : {
23249                     items : []
23250                 }
23251             };
23252             for (var i =0; i < this.specialChars.length; i++) {
23253                 smenu.menu.items.push({
23254                     
23255                     html: this.specialChars[i],
23256                     handler: function(a,b) {
23257                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23258                         //editor.insertAtCursor(a.html);
23259                         
23260                     },
23261                     tabIndex:-1
23262                 });
23263             }
23264             
23265             
23266             tb.add(smenu);
23267             
23268             
23269         }
23270         
23271         var cmenu = { };
23272         if (!this.disable.cleanStyles) {
23273             cmenu = {
23274                 cls: 'x-btn-icon x-btn-clear',
23275                 
23276                 menu : {
23277                     items : []
23278                 }
23279             };
23280             for (var i =0; i < this.cleanStyles.length; i++) {
23281                 cmenu.menu.items.push({
23282                     actiontype : this.cleanStyles[i],
23283                     html: 'Remove ' + this.cleanStyles[i],
23284                     handler: function(a,b) {
23285 //                        Roo.log(a);
23286 //                        Roo.log(b);
23287                         var c = Roo.get(editorcore.doc.body);
23288                         c.select('[style]').each(function(s) {
23289                             s.dom.style.removeProperty(a.actiontype);
23290                         });
23291                         editorcore.syncValue();
23292                     },
23293                     tabIndex:-1
23294                 });
23295             }
23296              cmenu.menu.items.push({
23297                 actiontype : 'tablewidths',
23298                 html: 'Remove Table Widths',
23299                 handler: function(a,b) {
23300                     editorcore.cleanTableWidths();
23301                     editorcore.syncValue();
23302                 },
23303                 tabIndex:-1
23304             });
23305             cmenu.menu.items.push({
23306                 actiontype : 'word',
23307                 html: 'Remove MS Word Formating',
23308                 handler: function(a,b) {
23309                     editorcore.cleanWord();
23310                     editorcore.syncValue();
23311                 },
23312                 tabIndex:-1
23313             });
23314             
23315             cmenu.menu.items.push({
23316                 actiontype : 'all',
23317                 html: 'Remove All Styles',
23318                 handler: function(a,b) {
23319                     
23320                     var c = Roo.get(editorcore.doc.body);
23321                     c.select('[style]').each(function(s) {
23322                         s.dom.removeAttribute('style');
23323                     });
23324                     editorcore.syncValue();
23325                 },
23326                 tabIndex:-1
23327             });
23328             
23329             cmenu.menu.items.push({
23330                 actiontype : 'all',
23331                 html: 'Remove All CSS Classes',
23332                 handler: function(a,b) {
23333                     
23334                     var c = Roo.get(editorcore.doc.body);
23335                     c.select('[class]').each(function(s) {
23336                         s.dom.removeAttribute('class');
23337                     });
23338                     editorcore.cleanWord();
23339                     editorcore.syncValue();
23340                 },
23341                 tabIndex:-1
23342             });
23343             
23344              cmenu.menu.items.push({
23345                 actiontype : 'tidy',
23346                 html: 'Tidy HTML Source',
23347                 handler: function(a,b) {
23348                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
23349                     editorcore.syncValue();
23350                 },
23351                 tabIndex:-1
23352             });
23353             
23354             
23355             tb.add(cmenu);
23356         }
23357          
23358         if (!this.disable.specialElements) {
23359             var semenu = {
23360                 text: "Other;",
23361                 cls: 'x-edit-none',
23362                 menu : {
23363                     items : []
23364                 }
23365             };
23366             for (var i =0; i < this.specialElements.length; i++) {
23367                 semenu.menu.items.push(
23368                     Roo.apply({ 
23369                         handler: function(a,b) {
23370                             editor.insertAtCursor(this.ihtml);
23371                         }
23372                     }, this.specialElements[i])
23373                 );
23374                     
23375             }
23376             
23377             tb.add(semenu);
23378             
23379             
23380         }
23381          
23382         
23383         if (this.btns) {
23384             for(var i =0; i< this.btns.length;i++) {
23385                 var b = Roo.factory(this.btns[i],Roo.form);
23386                 b.cls =  'x-edit-none';
23387                 
23388                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23389                     b.cls += ' x-init-enable';
23390                 }
23391                 
23392                 b.scope = editorcore;
23393                 tb.add(b);
23394             }
23395         
23396         }
23397         
23398         
23399         
23400         // disable everything...
23401         
23402         this.tb.items.each(function(item){
23403             
23404            if(
23405                 item.id != editorcore.frameId+ '-sourceedit' && 
23406                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23407             ){
23408                 
23409                 item.disable();
23410             }
23411         });
23412         this.rendered = true;
23413         
23414         // the all the btns;
23415         editor.on('editorevent', this.updateToolbar, this);
23416         // other toolbars need to implement this..
23417         //editor.on('editmodechange', this.updateToolbar, this);
23418     },
23419     
23420     
23421     relayBtnCmd : function(btn) {
23422         this.editorcore.relayCmd(btn.cmd);
23423     },
23424     // private used internally
23425     createLink : function(){
23426         Roo.log("create link?");
23427         var url = prompt(this.createLinkText, this.defaultLinkValue);
23428         if(url && url != 'http:/'+'/'){
23429             this.editorcore.relayCmd('createlink', url);
23430         }
23431     },
23432
23433     
23434     /**
23435      * Protected method that will not generally be called directly. It triggers
23436      * a toolbar update by reading the markup state of the current selection in the editor.
23437      */
23438     updateToolbar: function(){
23439
23440         if(!this.editorcore.activated){
23441             this.editor.onFirstFocus();
23442             return;
23443         }
23444
23445         var btns = this.tb.items.map, 
23446             doc = this.editorcore.doc,
23447             frameId = this.editorcore.frameId;
23448
23449         if(!this.disable.font && !Roo.isSafari){
23450             /*
23451             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23452             if(name != this.fontSelect.dom.value){
23453                 this.fontSelect.dom.value = name;
23454             }
23455             */
23456         }
23457         if(!this.disable.format){
23458             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23459             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23460             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23461             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23462         }
23463         if(!this.disable.alignments){
23464             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23465             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23466             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23467         }
23468         if(!Roo.isSafari && !this.disable.lists){
23469             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23470             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23471         }
23472         
23473         var ans = this.editorcore.getAllAncestors();
23474         if (this.formatCombo) {
23475             
23476             
23477             var store = this.formatCombo.store;
23478             this.formatCombo.setValue("");
23479             for (var i =0; i < ans.length;i++) {
23480                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23481                     // select it..
23482                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23483                     break;
23484                 }
23485             }
23486         }
23487         
23488         
23489         
23490         // hides menus... - so this cant be on a menu...
23491         Roo.menu.MenuMgr.hideAll();
23492
23493         //this.editorsyncValue();
23494     },
23495    
23496     
23497     createFontOptions : function(){
23498         var buf = [], fs = this.fontFamilies, ff, lc;
23499         
23500         
23501         
23502         for(var i = 0, len = fs.length; i< len; i++){
23503             ff = fs[i];
23504             lc = ff.toLowerCase();
23505             buf.push(
23506                 '<option value="',lc,'" style="font-family:',ff,';"',
23507                     (this.defaultFont == lc ? ' selected="true">' : '>'),
23508                     ff,
23509                 '</option>'
23510             );
23511         }
23512         return buf.join('');
23513     },
23514     
23515     toggleSourceEdit : function(sourceEditMode){
23516         
23517         Roo.log("toolbar toogle");
23518         if(sourceEditMode === undefined){
23519             sourceEditMode = !this.sourceEditMode;
23520         }
23521         this.sourceEditMode = sourceEditMode === true;
23522         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23523         // just toggle the button?
23524         if(btn.pressed !== this.sourceEditMode){
23525             btn.toggle(this.sourceEditMode);
23526             return;
23527         }
23528         
23529         if(sourceEditMode){
23530             Roo.log("disabling buttons");
23531             this.tb.items.each(function(item){
23532                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23533                     item.disable();
23534                 }
23535             });
23536           
23537         }else{
23538             Roo.log("enabling buttons");
23539             if(this.editorcore.initialized){
23540                 this.tb.items.each(function(item){
23541                     item.enable();
23542                 });
23543             }
23544             
23545         }
23546         Roo.log("calling toggole on editor");
23547         // tell the editor that it's been pressed..
23548         this.editor.toggleSourceEdit(sourceEditMode);
23549        
23550     },
23551      /**
23552      * Object collection of toolbar tooltips for the buttons in the editor. The key
23553      * is the command id associated with that button and the value is a valid QuickTips object.
23554      * For example:
23555 <pre><code>
23556 {
23557     bold : {
23558         title: 'Bold (Ctrl+B)',
23559         text: 'Make the selected text bold.',
23560         cls: 'x-html-editor-tip'
23561     },
23562     italic : {
23563         title: 'Italic (Ctrl+I)',
23564         text: 'Make the selected text italic.',
23565         cls: 'x-html-editor-tip'
23566     },
23567     ...
23568 </code></pre>
23569     * @type Object
23570      */
23571     buttonTips : {
23572         bold : {
23573             title: 'Bold (Ctrl+B)',
23574             text: 'Make the selected text bold.',
23575             cls: 'x-html-editor-tip'
23576         },
23577         italic : {
23578             title: 'Italic (Ctrl+I)',
23579             text: 'Make the selected text italic.',
23580             cls: 'x-html-editor-tip'
23581         },
23582         underline : {
23583             title: 'Underline (Ctrl+U)',
23584             text: 'Underline the selected text.',
23585             cls: 'x-html-editor-tip'
23586         },
23587         strikethrough : {
23588             title: 'Strikethrough',
23589             text: 'Strikethrough the selected text.',
23590             cls: 'x-html-editor-tip'
23591         },
23592         increasefontsize : {
23593             title: 'Grow Text',
23594             text: 'Increase the font size.',
23595             cls: 'x-html-editor-tip'
23596         },
23597         decreasefontsize : {
23598             title: 'Shrink Text',
23599             text: 'Decrease the font size.',
23600             cls: 'x-html-editor-tip'
23601         },
23602         backcolor : {
23603             title: 'Text Highlight Color',
23604             text: 'Change the background color of the selected text.',
23605             cls: 'x-html-editor-tip'
23606         },
23607         forecolor : {
23608             title: 'Font Color',
23609             text: 'Change the color of the selected text.',
23610             cls: 'x-html-editor-tip'
23611         },
23612         justifyleft : {
23613             title: 'Align Text Left',
23614             text: 'Align text to the left.',
23615             cls: 'x-html-editor-tip'
23616         },
23617         justifycenter : {
23618             title: 'Center Text',
23619             text: 'Center text in the editor.',
23620             cls: 'x-html-editor-tip'
23621         },
23622         justifyright : {
23623             title: 'Align Text Right',
23624             text: 'Align text to the right.',
23625             cls: 'x-html-editor-tip'
23626         },
23627         insertunorderedlist : {
23628             title: 'Bullet List',
23629             text: 'Start a bulleted list.',
23630             cls: 'x-html-editor-tip'
23631         },
23632         insertorderedlist : {
23633             title: 'Numbered List',
23634             text: 'Start a numbered list.',
23635             cls: 'x-html-editor-tip'
23636         },
23637         createlink : {
23638             title: 'Hyperlink',
23639             text: 'Make the selected text a hyperlink.',
23640             cls: 'x-html-editor-tip'
23641         },
23642         sourceedit : {
23643             title: 'Source Edit',
23644             text: 'Switch to source editing mode.',
23645             cls: 'x-html-editor-tip'
23646         }
23647     },
23648     // private
23649     onDestroy : function(){
23650         if(this.rendered){
23651             
23652             this.tb.items.each(function(item){
23653                 if(item.menu){
23654                     item.menu.removeAll();
23655                     if(item.menu.el){
23656                         item.menu.el.destroy();
23657                     }
23658                 }
23659                 item.destroy();
23660             });
23661              
23662         }
23663     },
23664     onFirstFocus: function() {
23665         this.tb.items.each(function(item){
23666            item.enable();
23667         });
23668     }
23669 });
23670
23671
23672
23673
23674 // <script type="text/javascript">
23675 /*
23676  * Based on
23677  * Ext JS Library 1.1.1
23678  * Copyright(c) 2006-2007, Ext JS, LLC.
23679  *  
23680  
23681  */
23682
23683  
23684 /**
23685  * @class Roo.form.HtmlEditor.ToolbarContext
23686  * Context Toolbar
23687  * 
23688  * Usage:
23689  *
23690  new Roo.form.HtmlEditor({
23691     ....
23692     toolbars : [
23693         { xtype: 'ToolbarStandard', styles : {} }
23694         { xtype: 'ToolbarContext', disable : {} }
23695     ]
23696 })
23697
23698      
23699  * 
23700  * @config : {Object} disable List of elements to disable.. (not done yet.)
23701  * @config : {Object} styles  Map of styles available.
23702  * 
23703  */
23704
23705 Roo.form.HtmlEditor.ToolbarContext = function(config)
23706 {
23707     
23708     Roo.apply(this, config);
23709     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23710     // dont call parent... till later.
23711     this.styles = this.styles || {};
23712 }
23713
23714  
23715
23716 Roo.form.HtmlEditor.ToolbarContext.types = {
23717     'IMG' : {
23718         width : {
23719             title: "Width",
23720             width: 40
23721         },
23722         height:  {
23723             title: "Height",
23724             width: 40
23725         },
23726         align: {
23727             title: "Align",
23728             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
23729             width : 80
23730             
23731         },
23732         border: {
23733             title: "Border",
23734             width: 40
23735         },
23736         alt: {
23737             title: "Alt",
23738             width: 120
23739         },
23740         src : {
23741             title: "Src",
23742             width: 220
23743         }
23744         
23745     },
23746     'A' : {
23747         name : {
23748             title: "Name",
23749             width: 50
23750         },
23751         target:  {
23752             title: "Target",
23753             width: 120
23754         },
23755         href:  {
23756             title: "Href",
23757             width: 220
23758         } // border?
23759         
23760     },
23761     'TABLE' : {
23762         rows : {
23763             title: "Rows",
23764             width: 20
23765         },
23766         cols : {
23767             title: "Cols",
23768             width: 20
23769         },
23770         width : {
23771             title: "Width",
23772             width: 40
23773         },
23774         height : {
23775             title: "Height",
23776             width: 40
23777         },
23778         border : {
23779             title: "Border",
23780             width: 20
23781         }
23782     },
23783     'TD' : {
23784         width : {
23785             title: "Width",
23786             width: 40
23787         },
23788         height : {
23789             title: "Height",
23790             width: 40
23791         },   
23792         align: {
23793             title: "Align",
23794             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
23795             width: 80
23796         },
23797         valign: {
23798             title: "Valign",
23799             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
23800             width: 80
23801         },
23802         colspan: {
23803             title: "Colspan",
23804             width: 20
23805             
23806         },
23807          'font-family'  : {
23808             title : "Font",
23809             style : 'fontFamily',
23810             displayField: 'display',
23811             optname : 'font-family',
23812             width: 140
23813         }
23814     },
23815     'INPUT' : {
23816         name : {
23817             title: "name",
23818             width: 120
23819         },
23820         value : {
23821             title: "Value",
23822             width: 120
23823         },
23824         width : {
23825             title: "Width",
23826             width: 40
23827         }
23828     },
23829     'LABEL' : {
23830         'for' : {
23831             title: "For",
23832             width: 120
23833         }
23834     },
23835     'TEXTAREA' : {
23836           name : {
23837             title: "name",
23838             width: 120
23839         },
23840         rows : {
23841             title: "Rows",
23842             width: 20
23843         },
23844         cols : {
23845             title: "Cols",
23846             width: 20
23847         }
23848     },
23849     'SELECT' : {
23850         name : {
23851             title: "name",
23852             width: 120
23853         },
23854         selectoptions : {
23855             title: "Options",
23856             width: 200
23857         }
23858     },
23859     
23860     // should we really allow this??
23861     // should this just be 
23862     'BODY' : {
23863         title : {
23864             title: "Title",
23865             width: 200,
23866             disabled : true
23867         }
23868     },
23869     'SPAN' : {
23870         'font-family'  : {
23871             title : "Font",
23872             style : 'fontFamily',
23873             displayField: 'display',
23874             optname : 'font-family',
23875             width: 140
23876         }
23877     },
23878     'DIV' : {
23879         'font-family'  : {
23880             title : "Font",
23881             style : 'fontFamily',
23882             displayField: 'display',
23883             optname : 'font-family',
23884             width: 140
23885         }
23886     },
23887      'P' : {
23888         'font-family'  : {
23889             title : "Font",
23890             style : 'fontFamily',
23891             displayField: 'display',
23892             optname : 'font-family',
23893             width: 140
23894         }
23895     },
23896     
23897     '*' : {
23898         // empty..
23899     }
23900
23901 };
23902
23903 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
23904 Roo.form.HtmlEditor.ToolbarContext.stores = false;
23905
23906 Roo.form.HtmlEditor.ToolbarContext.options = {
23907         'font-family'  : [ 
23908                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
23909                 [ 'Courier New', 'Courier New'],
23910                 [ 'Tahoma', 'Tahoma'],
23911                 [ 'Times New Roman,serif', 'Times'],
23912                 [ 'Verdana','Verdana' ]
23913         ]
23914 };
23915
23916 // fixme - these need to be configurable..
23917  
23918
23919 //Roo.form.HtmlEditor.ToolbarContext.types
23920
23921
23922 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
23923     
23924     tb: false,
23925     
23926     rendered: false,
23927     
23928     editor : false,
23929     editorcore : false,
23930     /**
23931      * @cfg {Object} disable  List of toolbar elements to disable
23932          
23933      */
23934     disable : false,
23935     /**
23936      * @cfg {Object} styles List of styles 
23937      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
23938      *
23939      * These must be defined in the page, so they get rendered correctly..
23940      * .headline { }
23941      * TD.underline { }
23942      * 
23943      */
23944     styles : false,
23945     
23946     options: false,
23947     
23948     toolbars : false,
23949     
23950     init : function(editor)
23951     {
23952         this.editor = editor;
23953         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23954         var editorcore = this.editorcore;
23955         
23956         var fid = editorcore.frameId;
23957         var etb = this;
23958         function btn(id, toggle, handler){
23959             var xid = fid + '-'+ id ;
23960             return {
23961                 id : xid,
23962                 cmd : id,
23963                 cls : 'x-btn-icon x-edit-'+id,
23964                 enableToggle:toggle !== false,
23965                 scope: editorcore, // was editor...
23966                 handler:handler||editorcore.relayBtnCmd,
23967                 clickEvent:'mousedown',
23968                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23969                 tabIndex:-1
23970             };
23971         }
23972         // create a new element.
23973         var wdiv = editor.wrap.createChild({
23974                 tag: 'div'
23975             }, editor.wrap.dom.firstChild.nextSibling, true);
23976         
23977         // can we do this more than once??
23978         
23979          // stop form submits
23980       
23981  
23982         // disable everything...
23983         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23984         this.toolbars = {};
23985            
23986         for (var i in  ty) {
23987           
23988             this.toolbars[i] = this.buildToolbar(ty[i],i);
23989         }
23990         this.tb = this.toolbars.BODY;
23991         this.tb.el.show();
23992         this.buildFooter();
23993         this.footer.show();
23994         editor.on('hide', function( ) { this.footer.hide() }, this);
23995         editor.on('show', function( ) { this.footer.show() }, this);
23996         
23997          
23998         this.rendered = true;
23999         
24000         // the all the btns;
24001         editor.on('editorevent', this.updateToolbar, this);
24002         // other toolbars need to implement this..
24003         //editor.on('editmodechange', this.updateToolbar, this);
24004     },
24005     
24006     
24007     
24008     /**
24009      * Protected method that will not generally be called directly. It triggers
24010      * a toolbar update by reading the markup state of the current selection in the editor.
24011      *
24012      * Note you can force an update by calling on('editorevent', scope, false)
24013      */
24014     updateToolbar: function(editor,ev,sel){
24015
24016         //Roo.log(ev);
24017         // capture mouse up - this is handy for selecting images..
24018         // perhaps should go somewhere else...
24019         if(!this.editorcore.activated){
24020              this.editor.onFirstFocus();
24021             return;
24022         }
24023         
24024         
24025         
24026         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
24027         // selectNode - might want to handle IE?
24028         if (ev &&
24029             (ev.type == 'mouseup' || ev.type == 'click' ) &&
24030             ev.target && ev.target.tagName == 'IMG') {
24031             // they have click on an image...
24032             // let's see if we can change the selection...
24033             sel = ev.target;
24034          
24035               var nodeRange = sel.ownerDocument.createRange();
24036             try {
24037                 nodeRange.selectNode(sel);
24038             } catch (e) {
24039                 nodeRange.selectNodeContents(sel);
24040             }
24041             //nodeRange.collapse(true);
24042             var s = this.editorcore.win.getSelection();
24043             s.removeAllRanges();
24044             s.addRange(nodeRange);
24045         }  
24046         
24047       
24048         var updateFooter = sel ? false : true;
24049         
24050         
24051         var ans = this.editorcore.getAllAncestors();
24052         
24053         // pick
24054         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24055         
24056         if (!sel) { 
24057             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
24058             sel = sel ? sel : this.editorcore.doc.body;
24059             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
24060             
24061         }
24062         // pick a menu that exists..
24063         var tn = sel.tagName.toUpperCase();
24064         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
24065         
24066         tn = sel.tagName.toUpperCase();
24067         
24068         var lastSel = this.tb.selectedNode;
24069         
24070         this.tb.selectedNode = sel;
24071         
24072         // if current menu does not match..
24073         
24074         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
24075                 
24076             this.tb.el.hide();
24077             ///console.log("show: " + tn);
24078             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
24079             this.tb.el.show();
24080             // update name
24081             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
24082             
24083             
24084             // update attributes
24085             if (this.tb.fields) {
24086                 this.tb.fields.each(function(e) {
24087                     if (e.stylename) {
24088                         e.setValue(sel.style[e.stylename]);
24089                         return;
24090                     } 
24091                    e.setValue(sel.getAttribute(e.attrname));
24092                 });
24093             }
24094             
24095             var hasStyles = false;
24096             for(var i in this.styles) {
24097                 hasStyles = true;
24098                 break;
24099             }
24100             
24101             // update styles
24102             if (hasStyles) { 
24103                 var st = this.tb.fields.item(0);
24104                 
24105                 st.store.removeAll();
24106                
24107                 
24108                 var cn = sel.className.split(/\s+/);
24109                 
24110                 var avs = [];
24111                 if (this.styles['*']) {
24112                     
24113                     Roo.each(this.styles['*'], function(v) {
24114                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24115                     });
24116                 }
24117                 if (this.styles[tn]) { 
24118                     Roo.each(this.styles[tn], function(v) {
24119                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24120                     });
24121                 }
24122                 
24123                 st.store.loadData(avs);
24124                 st.collapse();
24125                 st.setValue(cn);
24126             }
24127             // flag our selected Node.
24128             this.tb.selectedNode = sel;
24129            
24130            
24131             Roo.menu.MenuMgr.hideAll();
24132
24133         }
24134         
24135         if (!updateFooter) {
24136             //this.footDisp.dom.innerHTML = ''; 
24137             return;
24138         }
24139         // update the footer
24140         //
24141         var html = '';
24142         
24143         this.footerEls = ans.reverse();
24144         Roo.each(this.footerEls, function(a,i) {
24145             if (!a) { return; }
24146             html += html.length ? ' &gt; '  :  '';
24147             
24148             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
24149             
24150         });
24151        
24152         // 
24153         var sz = this.footDisp.up('td').getSize();
24154         this.footDisp.dom.style.width = (sz.width -10) + 'px';
24155         this.footDisp.dom.style.marginLeft = '5px';
24156         
24157         this.footDisp.dom.style.overflow = 'hidden';
24158         
24159         this.footDisp.dom.innerHTML = html;
24160             
24161         //this.editorsyncValue();
24162     },
24163      
24164     
24165    
24166        
24167     // private
24168     onDestroy : function(){
24169         if(this.rendered){
24170             
24171             this.tb.items.each(function(item){
24172                 if(item.menu){
24173                     item.menu.removeAll();
24174                     if(item.menu.el){
24175                         item.menu.el.destroy();
24176                     }
24177                 }
24178                 item.destroy();
24179             });
24180              
24181         }
24182     },
24183     onFirstFocus: function() {
24184         // need to do this for all the toolbars..
24185         this.tb.items.each(function(item){
24186            item.enable();
24187         });
24188     },
24189     buildToolbar: function(tlist, nm)
24190     {
24191         var editor = this.editor;
24192         var editorcore = this.editorcore;
24193          // create a new element.
24194         var wdiv = editor.wrap.createChild({
24195                 tag: 'div'
24196             }, editor.wrap.dom.firstChild.nextSibling, true);
24197         
24198        
24199         var tb = new Roo.Toolbar(wdiv);
24200         // add the name..
24201         
24202         tb.add(nm+ ":&nbsp;");
24203         
24204         var styles = [];
24205         for(var i in this.styles) {
24206             styles.push(i);
24207         }
24208         
24209         // styles...
24210         if (styles && styles.length) {
24211             
24212             // this needs a multi-select checkbox...
24213             tb.addField( new Roo.form.ComboBox({
24214                 store: new Roo.data.SimpleStore({
24215                     id : 'val',
24216                     fields: ['val', 'selected'],
24217                     data : [] 
24218                 }),
24219                 name : '-roo-edit-className',
24220                 attrname : 'className',
24221                 displayField: 'val',
24222                 typeAhead: false,
24223                 mode: 'local',
24224                 editable : false,
24225                 triggerAction: 'all',
24226                 emptyText:'Select Style',
24227                 selectOnFocus:true,
24228                 width: 130,
24229                 listeners : {
24230                     'select': function(c, r, i) {
24231                         // initial support only for on class per el..
24232                         tb.selectedNode.className =  r ? r.get('val') : '';
24233                         editorcore.syncValue();
24234                     }
24235                 }
24236     
24237             }));
24238         }
24239         
24240         var tbc = Roo.form.HtmlEditor.ToolbarContext;
24241         var tbops = tbc.options;
24242         
24243         for (var i in tlist) {
24244             
24245             var item = tlist[i];
24246             tb.add(item.title + ":&nbsp;");
24247             
24248             
24249             //optname == used so you can configure the options available..
24250             var opts = item.opts ? item.opts : false;
24251             if (item.optname) {
24252                 opts = tbops[item.optname];
24253            
24254             }
24255             
24256             if (opts) {
24257                 // opts == pulldown..
24258                 tb.addField( new Roo.form.ComboBox({
24259                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24260                         id : 'val',
24261                         fields: ['val', 'display'],
24262                         data : opts  
24263                     }),
24264                     name : '-roo-edit-' + i,
24265                     attrname : i,
24266                     stylename : item.style ? item.style : false,
24267                     displayField: item.displayField ? item.displayField : 'val',
24268                     valueField :  'val',
24269                     typeAhead: false,
24270                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
24271                     editable : false,
24272                     triggerAction: 'all',
24273                     emptyText:'Select',
24274                     selectOnFocus:true,
24275                     width: item.width ? item.width  : 130,
24276                     listeners : {
24277                         'select': function(c, r, i) {
24278                             if (c.stylename) {
24279                                 tb.selectedNode.style[c.stylename] =  r.get('val');
24280                                 return;
24281                             }
24282                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24283                         }
24284                     }
24285
24286                 }));
24287                 continue;
24288                     
24289                  
24290                 
24291                 tb.addField( new Roo.form.TextField({
24292                     name: i,
24293                     width: 100,
24294                     //allowBlank:false,
24295                     value: ''
24296                 }));
24297                 continue;
24298             }
24299             tb.addField( new Roo.form.TextField({
24300                 name: '-roo-edit-' + i,
24301                 attrname : i,
24302                 
24303                 width: item.width,
24304                 //allowBlank:true,
24305                 value: '',
24306                 listeners: {
24307                     'change' : function(f, nv, ov) {
24308                         tb.selectedNode.setAttribute(f.attrname, nv);
24309                         editorcore.syncValue();
24310                     }
24311                 }
24312             }));
24313              
24314         }
24315         
24316         var _this = this;
24317         
24318         if(nm == 'BODY'){
24319             tb.addSeparator();
24320         
24321             tb.addButton( {
24322                 text: 'Stylesheets',
24323
24324                 listeners : {
24325                     click : function ()
24326                     {
24327                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
24328                     }
24329                 }
24330             });
24331         }
24332         
24333         tb.addFill();
24334         tb.addButton( {
24335             text: 'Remove Tag',
24336     
24337             listeners : {
24338                 click : function ()
24339                 {
24340                     // remove
24341                     // undo does not work.
24342                      
24343                     var sn = tb.selectedNode;
24344                     
24345                     var pn = sn.parentNode;
24346                     
24347                     var stn =  sn.childNodes[0];
24348                     var en = sn.childNodes[sn.childNodes.length - 1 ];
24349                     while (sn.childNodes.length) {
24350                         var node = sn.childNodes[0];
24351                         sn.removeChild(node);
24352                         //Roo.log(node);
24353                         pn.insertBefore(node, sn);
24354                         
24355                     }
24356                     pn.removeChild(sn);
24357                     var range = editorcore.createRange();
24358         
24359                     range.setStart(stn,0);
24360                     range.setEnd(en,0); //????
24361                     //range.selectNode(sel);
24362                     
24363                     
24364                     var selection = editorcore.getSelection();
24365                     selection.removeAllRanges();
24366                     selection.addRange(range);
24367                     
24368                     
24369                     
24370                     //_this.updateToolbar(null, null, pn);
24371                     _this.updateToolbar(null, null, null);
24372                     _this.footDisp.dom.innerHTML = ''; 
24373                 }
24374             }
24375             
24376                     
24377                 
24378             
24379         });
24380         
24381         
24382         tb.el.on('click', function(e){
24383             e.preventDefault(); // what does this do?
24384         });
24385         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24386         tb.el.hide();
24387         tb.name = nm;
24388         // dont need to disable them... as they will get hidden
24389         return tb;
24390          
24391         
24392     },
24393     buildFooter : function()
24394     {
24395         
24396         var fel = this.editor.wrap.createChild();
24397         this.footer = new Roo.Toolbar(fel);
24398         // toolbar has scrolly on left / right?
24399         var footDisp= new Roo.Toolbar.Fill();
24400         var _t = this;
24401         this.footer.add(
24402             {
24403                 text : '&lt;',
24404                 xtype: 'Button',
24405                 handler : function() {
24406                     _t.footDisp.scrollTo('left',0,true)
24407                 }
24408             }
24409         );
24410         this.footer.add( footDisp );
24411         this.footer.add( 
24412             {
24413                 text : '&gt;',
24414                 xtype: 'Button',
24415                 handler : function() {
24416                     // no animation..
24417                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24418                 }
24419             }
24420         );
24421         var fel = Roo.get(footDisp.el);
24422         fel.addClass('x-editor-context');
24423         this.footDispWrap = fel; 
24424         this.footDispWrap.overflow  = 'hidden';
24425         
24426         this.footDisp = fel.createChild();
24427         this.footDispWrap.on('click', this.onContextClick, this)
24428         
24429         
24430     },
24431     onContextClick : function (ev,dom)
24432     {
24433         ev.preventDefault();
24434         var  cn = dom.className;
24435         //Roo.log(cn);
24436         if (!cn.match(/x-ed-loc-/)) {
24437             return;
24438         }
24439         var n = cn.split('-').pop();
24440         var ans = this.footerEls;
24441         var sel = ans[n];
24442         
24443          // pick
24444         var range = this.editorcore.createRange();
24445         
24446         range.selectNodeContents(sel);
24447         //range.selectNode(sel);
24448         
24449         
24450         var selection = this.editorcore.getSelection();
24451         selection.removeAllRanges();
24452         selection.addRange(range);
24453         
24454         
24455         
24456         this.updateToolbar(null, null, sel);
24457         
24458         
24459     }
24460     
24461     
24462     
24463     
24464     
24465 });
24466
24467
24468
24469
24470
24471 /*
24472  * Based on:
24473  * Ext JS Library 1.1.1
24474  * Copyright(c) 2006-2007, Ext JS, LLC.
24475  *
24476  * Originally Released Under LGPL - original licence link has changed is not relivant.
24477  *
24478  * Fork - LGPL
24479  * <script type="text/javascript">
24480  */
24481  
24482 /**
24483  * @class Roo.form.BasicForm
24484  * @extends Roo.util.Observable
24485  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24486  * @constructor
24487  * @param {String/HTMLElement/Roo.Element} el The form element or its id
24488  * @param {Object} config Configuration options
24489  */
24490 Roo.form.BasicForm = function(el, config){
24491     this.allItems = [];
24492     this.childForms = [];
24493     Roo.apply(this, config);
24494     /*
24495      * The Roo.form.Field items in this form.
24496      * @type MixedCollection
24497      */
24498      
24499      
24500     this.items = new Roo.util.MixedCollection(false, function(o){
24501         return o.id || (o.id = Roo.id());
24502     });
24503     this.addEvents({
24504         /**
24505          * @event beforeaction
24506          * Fires before any action is performed. Return false to cancel the action.
24507          * @param {Form} this
24508          * @param {Action} action The action to be performed
24509          */
24510         beforeaction: true,
24511         /**
24512          * @event actionfailed
24513          * Fires when an action fails.
24514          * @param {Form} this
24515          * @param {Action} action The action that failed
24516          */
24517         actionfailed : true,
24518         /**
24519          * @event actioncomplete
24520          * Fires when an action is completed.
24521          * @param {Form} this
24522          * @param {Action} action The action that completed
24523          */
24524         actioncomplete : true
24525     });
24526     if(el){
24527         this.initEl(el);
24528     }
24529     Roo.form.BasicForm.superclass.constructor.call(this);
24530     
24531     Roo.form.BasicForm.popover.apply();
24532 };
24533
24534 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24535     /**
24536      * @cfg {String} method
24537      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24538      */
24539     /**
24540      * @cfg {DataReader} reader
24541      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24542      * This is optional as there is built-in support for processing JSON.
24543      */
24544     /**
24545      * @cfg {DataReader} errorReader
24546      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24547      * This is completely optional as there is built-in support for processing JSON.
24548      */
24549     /**
24550      * @cfg {String} url
24551      * The URL to use for form actions if one isn't supplied in the action options.
24552      */
24553     /**
24554      * @cfg {Boolean} fileUpload
24555      * Set to true if this form is a file upload.
24556      */
24557      
24558     /**
24559      * @cfg {Object} baseParams
24560      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24561      */
24562      /**
24563      
24564     /**
24565      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24566      */
24567     timeout: 30,
24568
24569     // private
24570     activeAction : null,
24571
24572     /**
24573      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24574      * or setValues() data instead of when the form was first created.
24575      */
24576     trackResetOnLoad : false,
24577     
24578     
24579     /**
24580      * childForms - used for multi-tab forms
24581      * @type {Array}
24582      */
24583     childForms : false,
24584     
24585     /**
24586      * allItems - full list of fields.
24587      * @type {Array}
24588      */
24589     allItems : false,
24590     
24591     /**
24592      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24593      * element by passing it or its id or mask the form itself by passing in true.
24594      * @type Mixed
24595      */
24596     waitMsgTarget : false,
24597     
24598     /**
24599      * @type Boolean
24600      */
24601     disableMask : false,
24602     
24603     /**
24604      * @cfg {Boolean} errorMask (true|false) default false
24605      */
24606     errorMask : false,
24607     
24608     /**
24609      * @cfg {Number} maskOffset Default 100
24610      */
24611     maskOffset : 100,
24612
24613     // private
24614     initEl : function(el){
24615         this.el = Roo.get(el);
24616         this.id = this.el.id || Roo.id();
24617         this.el.on('submit', this.onSubmit, this);
24618         this.el.addClass('x-form');
24619     },
24620
24621     // private
24622     onSubmit : function(e){
24623         e.stopEvent();
24624     },
24625
24626     /**
24627      * Returns true if client-side validation on the form is successful.
24628      * @return Boolean
24629      */
24630     isValid : function(){
24631         var valid = true;
24632         var target = false;
24633         this.items.each(function(f){
24634             if(f.validate()){
24635                 return;
24636             }
24637             
24638             valid = false;
24639                 
24640             if(!target && f.el.isVisible(true)){
24641                 target = f;
24642             }
24643         });
24644         
24645         if(this.errorMask && !valid){
24646             Roo.form.BasicForm.popover.mask(this, target);
24647         }
24648         
24649         return valid;
24650     },
24651     /**
24652      * Returns array of invalid form fields.
24653      * @return Array
24654      */
24655     
24656     invalidFields : function()
24657     {
24658         var ret = [];
24659         this.items.each(function(f){
24660             if(f.validate()){
24661                 return;
24662             }
24663             ret.push(f);
24664             
24665         });
24666         
24667         return ret;
24668     },
24669     
24670     
24671     /**
24672      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
24673      * @return Boolean
24674      */
24675     isDirty : function(){
24676         var dirty = false;
24677         this.items.each(function(f){
24678            if(f.isDirty()){
24679                dirty = true;
24680                return false;
24681            }
24682         });
24683         return dirty;
24684     },
24685     
24686     /**
24687      * Returns true if any fields in this form have changed since their original load. (New version)
24688      * @return Boolean
24689      */
24690     
24691     hasChanged : function()
24692     {
24693         var dirty = false;
24694         this.items.each(function(f){
24695            if(f.hasChanged()){
24696                dirty = true;
24697                return false;
24698            }
24699         });
24700         return dirty;
24701         
24702     },
24703     /**
24704      * Resets all hasChanged to 'false' -
24705      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24706      * So hasChanged storage is only to be used for this purpose
24707      * @return Boolean
24708      */
24709     resetHasChanged : function()
24710     {
24711         this.items.each(function(f){
24712            f.resetHasChanged();
24713         });
24714         
24715     },
24716     
24717     
24718     /**
24719      * Performs a predefined action (submit or load) or custom actions you define on this form.
24720      * @param {String} actionName The name of the action type
24721      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
24722      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24723      * accept other config options):
24724      * <pre>
24725 Property          Type             Description
24726 ----------------  ---------------  ----------------------------------------------------------------------------------
24727 url               String           The url for the action (defaults to the form's url)
24728 method            String           The form method to use (defaults to the form's method, or POST if not defined)
24729 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
24730 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
24731                                    validate the form on the client (defaults to false)
24732      * </pre>
24733      * @return {BasicForm} this
24734      */
24735     doAction : function(action, options){
24736         if(typeof action == 'string'){
24737             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
24738         }
24739         if(this.fireEvent('beforeaction', this, action) !== false){
24740             this.beforeAction(action);
24741             action.run.defer(100, action);
24742         }
24743         return this;
24744     },
24745
24746     /**
24747      * Shortcut to do a submit action.
24748      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24749      * @return {BasicForm} this
24750      */
24751     submit : function(options){
24752         this.doAction('submit', options);
24753         return this;
24754     },
24755
24756     /**
24757      * Shortcut to do a load action.
24758      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24759      * @return {BasicForm} this
24760      */
24761     load : function(options){
24762         this.doAction('load', options);
24763         return this;
24764     },
24765
24766     /**
24767      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
24768      * @param {Record} record The record to edit
24769      * @return {BasicForm} this
24770      */
24771     updateRecord : function(record){
24772         record.beginEdit();
24773         var fs = record.fields;
24774         fs.each(function(f){
24775             var field = this.findField(f.name);
24776             if(field){
24777                 record.set(f.name, field.getValue());
24778             }
24779         }, this);
24780         record.endEdit();
24781         return this;
24782     },
24783
24784     /**
24785      * Loads an Roo.data.Record into this form.
24786      * @param {Record} record The record to load
24787      * @return {BasicForm} this
24788      */
24789     loadRecord : function(record){
24790         this.setValues(record.data);
24791         return this;
24792     },
24793
24794     // private
24795     beforeAction : function(action){
24796         var o = action.options;
24797         
24798         if(!this.disableMask) {
24799             if(this.waitMsgTarget === true){
24800                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
24801             }else if(this.waitMsgTarget){
24802                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
24803                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
24804             }else {
24805                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
24806             }
24807         }
24808         
24809          
24810     },
24811
24812     // private
24813     afterAction : function(action, success){
24814         this.activeAction = null;
24815         var o = action.options;
24816         
24817         if(!this.disableMask) {
24818             if(this.waitMsgTarget === true){
24819                 this.el.unmask();
24820             }else if(this.waitMsgTarget){
24821                 this.waitMsgTarget.unmask();
24822             }else{
24823                 Roo.MessageBox.updateProgress(1);
24824                 Roo.MessageBox.hide();
24825             }
24826         }
24827         
24828         if(success){
24829             if(o.reset){
24830                 this.reset();
24831             }
24832             Roo.callback(o.success, o.scope, [this, action]);
24833             this.fireEvent('actioncomplete', this, action);
24834             
24835         }else{
24836             
24837             // failure condition..
24838             // we have a scenario where updates need confirming.
24839             // eg. if a locking scenario exists..
24840             // we look for { errors : { needs_confirm : true }} in the response.
24841             if (
24842                 (typeof(action.result) != 'undefined')  &&
24843                 (typeof(action.result.errors) != 'undefined')  &&
24844                 (typeof(action.result.errors.needs_confirm) != 'undefined')
24845            ){
24846                 var _t = this;
24847                 Roo.MessageBox.confirm(
24848                     "Change requires confirmation",
24849                     action.result.errorMsg,
24850                     function(r) {
24851                         if (r != 'yes') {
24852                             return;
24853                         }
24854                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
24855                     }
24856                     
24857                 );
24858                 
24859                 
24860                 
24861                 return;
24862             }
24863             
24864             Roo.callback(o.failure, o.scope, [this, action]);
24865             // show an error message if no failed handler is set..
24866             if (!this.hasListener('actionfailed')) {
24867                 Roo.MessageBox.alert("Error",
24868                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
24869                         action.result.errorMsg :
24870                         "Saving Failed, please check your entries or try again"
24871                 );
24872             }
24873             
24874             this.fireEvent('actionfailed', this, action);
24875         }
24876         
24877     },
24878
24879     /**
24880      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
24881      * @param {String} id The value to search for
24882      * @return Field
24883      */
24884     findField : function(id){
24885         var field = this.items.get(id);
24886         if(!field){
24887             this.items.each(function(f){
24888                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
24889                     field = f;
24890                     return false;
24891                 }
24892             });
24893         }
24894         return field || null;
24895     },
24896
24897     /**
24898      * Add a secondary form to this one, 
24899      * Used to provide tabbed forms. One form is primary, with hidden values 
24900      * which mirror the elements from the other forms.
24901      * 
24902      * @param {Roo.form.Form} form to add.
24903      * 
24904      */
24905     addForm : function(form)
24906     {
24907        
24908         if (this.childForms.indexOf(form) > -1) {
24909             // already added..
24910             return;
24911         }
24912         this.childForms.push(form);
24913         var n = '';
24914         Roo.each(form.allItems, function (fe) {
24915             
24916             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
24917             if (this.findField(n)) { // already added..
24918                 return;
24919             }
24920             var add = new Roo.form.Hidden({
24921                 name : n
24922             });
24923             add.render(this.el);
24924             
24925             this.add( add );
24926         }, this);
24927         
24928     },
24929     /**
24930      * Mark fields in this form invalid in bulk.
24931      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
24932      * @return {BasicForm} this
24933      */
24934     markInvalid : function(errors){
24935         if(errors instanceof Array){
24936             for(var i = 0, len = errors.length; i < len; i++){
24937                 var fieldError = errors[i];
24938                 var f = this.findField(fieldError.id);
24939                 if(f){
24940                     f.markInvalid(fieldError.msg);
24941                 }
24942             }
24943         }else{
24944             var field, id;
24945             for(id in errors){
24946                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
24947                     field.markInvalid(errors[id]);
24948                 }
24949             }
24950         }
24951         Roo.each(this.childForms || [], function (f) {
24952             f.markInvalid(errors);
24953         });
24954         
24955         return this;
24956     },
24957
24958     /**
24959      * Set values for fields in this form in bulk.
24960      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
24961      * @return {BasicForm} this
24962      */
24963     setValues : function(values){
24964         if(values instanceof Array){ // array of objects
24965             for(var i = 0, len = values.length; i < len; i++){
24966                 var v = values[i];
24967                 var f = this.findField(v.id);
24968                 if(f){
24969                     f.setValue(v.value);
24970                     if(this.trackResetOnLoad){
24971                         f.originalValue = f.getValue();
24972                     }
24973                 }
24974             }
24975         }else{ // object hash
24976             var field, id;
24977             for(id in values){
24978                 if(typeof values[id] != 'function' && (field = this.findField(id))){
24979                     
24980                     if (field.setFromData && 
24981                         field.valueField && 
24982                         field.displayField &&
24983                         // combos' with local stores can 
24984                         // be queried via setValue()
24985                         // to set their value..
24986                         (field.store && !field.store.isLocal)
24987                         ) {
24988                         // it's a combo
24989                         var sd = { };
24990                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
24991                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
24992                         field.setFromData(sd);
24993                         
24994                     } else {
24995                         field.setValue(values[id]);
24996                     }
24997                     
24998                     
24999                     if(this.trackResetOnLoad){
25000                         field.originalValue = field.getValue();
25001                     }
25002                 }
25003             }
25004         }
25005         this.resetHasChanged();
25006         
25007         
25008         Roo.each(this.childForms || [], function (f) {
25009             f.setValues(values);
25010             f.resetHasChanged();
25011         });
25012                 
25013         return this;
25014     },
25015  
25016     /**
25017      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
25018      * they are returned as an array.
25019      * @param {Boolean} asString
25020      * @return {Object}
25021      */
25022     getValues : function(asString){
25023         if (this.childForms) {
25024             // copy values from the child forms
25025             Roo.each(this.childForms, function (f) {
25026                 this.setValues(f.getValues());
25027             }, this);
25028         }
25029         
25030         // use formdata
25031         if (typeof(FormData) != 'undefined' && asString !== true) {
25032             // this relies on a 'recent' version of chrome apparently...
25033             try {
25034                 var fd = (new FormData(this.el.dom)).entries();
25035                 var ret = {};
25036                 var ent = fd.next();
25037                 while (!ent.done) {
25038                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
25039                     ent = fd.next();
25040                 };
25041                 return ret;
25042             } catch(e) {
25043                 
25044             }
25045             
25046         }
25047         
25048         
25049         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
25050         if(asString === true){
25051             return fs;
25052         }
25053         return Roo.urlDecode(fs);
25054     },
25055     
25056     /**
25057      * Returns the fields in this form as an object with key/value pairs. 
25058      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
25059      * @return {Object}
25060      */
25061     getFieldValues : function(with_hidden)
25062     {
25063         if (this.childForms) {
25064             // copy values from the child forms
25065             // should this call getFieldValues - probably not as we do not currently copy
25066             // hidden fields when we generate..
25067             Roo.each(this.childForms, function (f) {
25068                 this.setValues(f.getValues());
25069             }, this);
25070         }
25071         
25072         var ret = {};
25073         this.items.each(function(f){
25074             if (!f.getName()) {
25075                 return;
25076             }
25077             var v = f.getValue();
25078             if (f.inputType =='radio') {
25079                 if (typeof(ret[f.getName()]) == 'undefined') {
25080                     ret[f.getName()] = ''; // empty..
25081                 }
25082                 
25083                 if (!f.el.dom.checked) {
25084                     return;
25085                     
25086                 }
25087                 v = f.el.dom.value;
25088                 
25089             }
25090             
25091             // not sure if this supported any more..
25092             if ((typeof(v) == 'object') && f.getRawValue) {
25093                 v = f.getRawValue() ; // dates..
25094             }
25095             // combo boxes where name != hiddenName...
25096             if (f.name != f.getName()) {
25097                 ret[f.name] = f.getRawValue();
25098             }
25099             ret[f.getName()] = v;
25100         });
25101         
25102         return ret;
25103     },
25104
25105     /**
25106      * Clears all invalid messages in this form.
25107      * @return {BasicForm} this
25108      */
25109     clearInvalid : function(){
25110         this.items.each(function(f){
25111            f.clearInvalid();
25112         });
25113         
25114         Roo.each(this.childForms || [], function (f) {
25115             f.clearInvalid();
25116         });
25117         
25118         
25119         return this;
25120     },
25121
25122     /**
25123      * Resets this form.
25124      * @return {BasicForm} this
25125      */
25126     reset : function(){
25127         this.items.each(function(f){
25128             f.reset();
25129         });
25130         
25131         Roo.each(this.childForms || [], function (f) {
25132             f.reset();
25133         });
25134         this.resetHasChanged();
25135         
25136         return this;
25137     },
25138
25139     /**
25140      * Add Roo.form components to this form.
25141      * @param {Field} field1
25142      * @param {Field} field2 (optional)
25143      * @param {Field} etc (optional)
25144      * @return {BasicForm} this
25145      */
25146     add : function(){
25147         this.items.addAll(Array.prototype.slice.call(arguments, 0));
25148         return this;
25149     },
25150
25151
25152     /**
25153      * Removes a field from the items collection (does NOT remove its markup).
25154      * @param {Field} field
25155      * @return {BasicForm} this
25156      */
25157     remove : function(field){
25158         this.items.remove(field);
25159         return this;
25160     },
25161
25162     /**
25163      * Looks at the fields in this form, checks them for an id attribute,
25164      * and calls applyTo on the existing dom element with that id.
25165      * @return {BasicForm} this
25166      */
25167     render : function(){
25168         this.items.each(function(f){
25169             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
25170                 f.applyTo(f.id);
25171             }
25172         });
25173         return this;
25174     },
25175
25176     /**
25177      * Calls {@link Ext#apply} for all fields in this form with the passed object.
25178      * @param {Object} values
25179      * @return {BasicForm} this
25180      */
25181     applyToFields : function(o){
25182         this.items.each(function(f){
25183            Roo.apply(f, o);
25184         });
25185         return this;
25186     },
25187
25188     /**
25189      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
25190      * @param {Object} values
25191      * @return {BasicForm} this
25192      */
25193     applyIfToFields : function(o){
25194         this.items.each(function(f){
25195            Roo.applyIf(f, o);
25196         });
25197         return this;
25198     }
25199 });
25200
25201 // back compat
25202 Roo.BasicForm = Roo.form.BasicForm;
25203
25204 Roo.apply(Roo.form.BasicForm, {
25205     
25206     popover : {
25207         
25208         padding : 5,
25209         
25210         isApplied : false,
25211         
25212         isMasked : false,
25213         
25214         form : false,
25215         
25216         target : false,
25217         
25218         intervalID : false,
25219         
25220         maskEl : false,
25221         
25222         apply : function()
25223         {
25224             if(this.isApplied){
25225                 return;
25226             }
25227             
25228             this.maskEl = {
25229                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
25230                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
25231                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
25232                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
25233             };
25234             
25235             this.maskEl.top.enableDisplayMode("block");
25236             this.maskEl.left.enableDisplayMode("block");
25237             this.maskEl.bottom.enableDisplayMode("block");
25238             this.maskEl.right.enableDisplayMode("block");
25239             
25240             Roo.get(document.body).on('click', function(){
25241                 this.unmask();
25242             }, this);
25243             
25244             Roo.get(document.body).on('touchstart', function(){
25245                 this.unmask();
25246             }, this);
25247             
25248             this.isApplied = true
25249         },
25250         
25251         mask : function(form, target)
25252         {
25253             this.form = form;
25254             
25255             this.target = target;
25256             
25257             if(!this.form.errorMask || !target.el){
25258                 return;
25259             }
25260             
25261             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
25262             
25263             var ot = this.target.el.calcOffsetsTo(scrollable);
25264             
25265             var scrollTo = ot[1] - this.form.maskOffset;
25266             
25267             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
25268             
25269             scrollable.scrollTo('top', scrollTo);
25270             
25271             var el = this.target.wrap || this.target.el;
25272             
25273             var box = el.getBox();
25274             
25275             this.maskEl.top.setStyle('position', 'absolute');
25276             this.maskEl.top.setStyle('z-index', 10000);
25277             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
25278             this.maskEl.top.setLeft(0);
25279             this.maskEl.top.setTop(0);
25280             this.maskEl.top.show();
25281             
25282             this.maskEl.left.setStyle('position', 'absolute');
25283             this.maskEl.left.setStyle('z-index', 10000);
25284             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
25285             this.maskEl.left.setLeft(0);
25286             this.maskEl.left.setTop(box.y - this.padding);
25287             this.maskEl.left.show();
25288
25289             this.maskEl.bottom.setStyle('position', 'absolute');
25290             this.maskEl.bottom.setStyle('z-index', 10000);
25291             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
25292             this.maskEl.bottom.setLeft(0);
25293             this.maskEl.bottom.setTop(box.bottom + this.padding);
25294             this.maskEl.bottom.show();
25295
25296             this.maskEl.right.setStyle('position', 'absolute');
25297             this.maskEl.right.setStyle('z-index', 10000);
25298             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
25299             this.maskEl.right.setLeft(box.right + this.padding);
25300             this.maskEl.right.setTop(box.y - this.padding);
25301             this.maskEl.right.show();
25302
25303             this.intervalID = window.setInterval(function() {
25304                 Roo.form.BasicForm.popover.unmask();
25305             }, 10000);
25306
25307             window.onwheel = function(){ return false;};
25308             
25309             (function(){ this.isMasked = true; }).defer(500, this);
25310             
25311         },
25312         
25313         unmask : function()
25314         {
25315             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
25316                 return;
25317             }
25318             
25319             this.maskEl.top.setStyle('position', 'absolute');
25320             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
25321             this.maskEl.top.hide();
25322
25323             this.maskEl.left.setStyle('position', 'absolute');
25324             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
25325             this.maskEl.left.hide();
25326
25327             this.maskEl.bottom.setStyle('position', 'absolute');
25328             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
25329             this.maskEl.bottom.hide();
25330
25331             this.maskEl.right.setStyle('position', 'absolute');
25332             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
25333             this.maskEl.right.hide();
25334             
25335             window.onwheel = function(){ return true;};
25336             
25337             if(this.intervalID){
25338                 window.clearInterval(this.intervalID);
25339                 this.intervalID = false;
25340             }
25341             
25342             this.isMasked = false;
25343             
25344         }
25345         
25346     }
25347     
25348 });/*
25349  * Based on:
25350  * Ext JS Library 1.1.1
25351  * Copyright(c) 2006-2007, Ext JS, LLC.
25352  *
25353  * Originally Released Under LGPL - original licence link has changed is not relivant.
25354  *
25355  * Fork - LGPL
25356  * <script type="text/javascript">
25357  */
25358
25359 /**
25360  * @class Roo.form.Form
25361  * @extends Roo.form.BasicForm
25362  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
25363  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
25364  * @constructor
25365  * @param {Object} config Configuration options
25366  */
25367 Roo.form.Form = function(config){
25368     var xitems =  [];
25369     if (config.items) {
25370         xitems = config.items;
25371         delete config.items;
25372     }
25373    
25374     
25375     Roo.form.Form.superclass.constructor.call(this, null, config);
25376     this.url = this.url || this.action;
25377     if(!this.root){
25378         this.root = new Roo.form.Layout(Roo.applyIf({
25379             id: Roo.id()
25380         }, config));
25381     }
25382     this.active = this.root;
25383     /**
25384      * Array of all the buttons that have been added to this form via {@link addButton}
25385      * @type Array
25386      */
25387     this.buttons = [];
25388     this.allItems = [];
25389     this.addEvents({
25390         /**
25391          * @event clientvalidation
25392          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25393          * @param {Form} this
25394          * @param {Boolean} valid true if the form has passed client-side validation
25395          */
25396         clientvalidation: true,
25397         /**
25398          * @event rendered
25399          * Fires when the form is rendered
25400          * @param {Roo.form.Form} form
25401          */
25402         rendered : true
25403     });
25404     
25405     if (this.progressUrl) {
25406             // push a hidden field onto the list of fields..
25407             this.addxtype( {
25408                     xns: Roo.form, 
25409                     xtype : 'Hidden', 
25410                     name : 'UPLOAD_IDENTIFIER' 
25411             });
25412         }
25413         
25414     
25415     Roo.each(xitems, this.addxtype, this);
25416     
25417 };
25418
25419 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25420      /**
25421      * @cfg {Roo.Button} buttons[] buttons at bottom of form
25422      */
25423     
25424     /**
25425      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25426      */
25427     /**
25428      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25429      */
25430     /**
25431      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25432      */
25433     buttonAlign:'center',
25434
25435     /**
25436      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25437      */
25438     minButtonWidth:75,
25439
25440     /**
25441      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25442      * This property cascades to child containers if not set.
25443      */
25444     labelAlign:'left',
25445
25446     /**
25447      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25448      * fires a looping event with that state. This is required to bind buttons to the valid
25449      * state using the config value formBind:true on the button.
25450      */
25451     monitorValid : false,
25452
25453     /**
25454      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25455      */
25456     monitorPoll : 200,
25457     
25458     /**
25459      * @cfg {String} progressUrl - Url to return progress data 
25460      */
25461     
25462     progressUrl : false,
25463     /**
25464      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25465      * sending a formdata with extra parameters - eg uploaded elements.
25466      */
25467     
25468     formData : false,
25469     
25470     /**
25471      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25472      * fields are added and the column is closed. If no fields are passed the column remains open
25473      * until end() is called.
25474      * @param {Object} config The config to pass to the column
25475      * @param {Field} field1 (optional)
25476      * @param {Field} field2 (optional)
25477      * @param {Field} etc (optional)
25478      * @return Column The column container object
25479      */
25480     column : function(c){
25481         var col = new Roo.form.Column(c);
25482         this.start(col);
25483         if(arguments.length > 1){ // duplicate code required because of Opera
25484             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25485             this.end();
25486         }
25487         return col;
25488     },
25489
25490     /**
25491      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25492      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25493      * until end() is called.
25494      * @param {Object} config The config to pass to the fieldset
25495      * @param {Field} field1 (optional)
25496      * @param {Field} field2 (optional)
25497      * @param {Field} etc (optional)
25498      * @return FieldSet The fieldset container object
25499      */
25500     fieldset : function(c){
25501         var fs = new Roo.form.FieldSet(c);
25502         this.start(fs);
25503         if(arguments.length > 1){ // duplicate code required because of Opera
25504             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25505             this.end();
25506         }
25507         return fs;
25508     },
25509
25510     /**
25511      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25512      * fields are added and the container is closed. If no fields are passed the container remains open
25513      * until end() is called.
25514      * @param {Object} config The config to pass to the Layout
25515      * @param {Field} field1 (optional)
25516      * @param {Field} field2 (optional)
25517      * @param {Field} etc (optional)
25518      * @return Layout The container object
25519      */
25520     container : function(c){
25521         var l = new Roo.form.Layout(c);
25522         this.start(l);
25523         if(arguments.length > 1){ // duplicate code required because of Opera
25524             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25525             this.end();
25526         }
25527         return l;
25528     },
25529
25530     /**
25531      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25532      * @param {Object} container A Roo.form.Layout or subclass of Layout
25533      * @return {Form} this
25534      */
25535     start : function(c){
25536         // cascade label info
25537         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25538         this.active.stack.push(c);
25539         c.ownerCt = this.active;
25540         this.active = c;
25541         return this;
25542     },
25543
25544     /**
25545      * Closes the current open container
25546      * @return {Form} this
25547      */
25548     end : function(){
25549         if(this.active == this.root){
25550             return this;
25551         }
25552         this.active = this.active.ownerCt;
25553         return this;
25554     },
25555
25556     /**
25557      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25558      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25559      * as the label of the field.
25560      * @param {Field} field1
25561      * @param {Field} field2 (optional)
25562      * @param {Field} etc. (optional)
25563      * @return {Form} this
25564      */
25565     add : function(){
25566         this.active.stack.push.apply(this.active.stack, arguments);
25567         this.allItems.push.apply(this.allItems,arguments);
25568         var r = [];
25569         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25570             if(a[i].isFormField){
25571                 r.push(a[i]);
25572             }
25573         }
25574         if(r.length > 0){
25575             Roo.form.Form.superclass.add.apply(this, r);
25576         }
25577         return this;
25578     },
25579     
25580
25581     
25582     
25583     
25584      /**
25585      * Find any element that has been added to a form, using it's ID or name
25586      * This can include framesets, columns etc. along with regular fields..
25587      * @param {String} id - id or name to find.
25588      
25589      * @return {Element} e - or false if nothing found.
25590      */
25591     findbyId : function(id)
25592     {
25593         var ret = false;
25594         if (!id) {
25595             return ret;
25596         }
25597         Roo.each(this.allItems, function(f){
25598             if (f.id == id || f.name == id ){
25599                 ret = f;
25600                 return false;
25601             }
25602         });
25603         return ret;
25604     },
25605
25606     
25607     
25608     /**
25609      * Render this form into the passed container. This should only be called once!
25610      * @param {String/HTMLElement/Element} container The element this component should be rendered into
25611      * @return {Form} this
25612      */
25613     render : function(ct)
25614     {
25615         
25616         
25617         
25618         ct = Roo.get(ct);
25619         var o = this.autoCreate || {
25620             tag: 'form',
25621             method : this.method || 'POST',
25622             id : this.id || Roo.id()
25623         };
25624         this.initEl(ct.createChild(o));
25625
25626         this.root.render(this.el);
25627         
25628        
25629              
25630         this.items.each(function(f){
25631             f.render('x-form-el-'+f.id);
25632         });
25633
25634         if(this.buttons.length > 0){
25635             // tables are required to maintain order and for correct IE layout
25636             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25637                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25638                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25639             }}, null, true);
25640             var tr = tb.getElementsByTagName('tr')[0];
25641             for(var i = 0, len = this.buttons.length; i < len; i++) {
25642                 var b = this.buttons[i];
25643                 var td = document.createElement('td');
25644                 td.className = 'x-form-btn-td';
25645                 b.render(tr.appendChild(td));
25646             }
25647         }
25648         if(this.monitorValid){ // initialize after render
25649             this.startMonitoring();
25650         }
25651         this.fireEvent('rendered', this);
25652         return this;
25653     },
25654
25655     /**
25656      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25657      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25658      * object or a valid Roo.DomHelper element config
25659      * @param {Function} handler The function called when the button is clicked
25660      * @param {Object} scope (optional) The scope of the handler function
25661      * @return {Roo.Button}
25662      */
25663     addButton : function(config, handler, scope){
25664         var bc = {
25665             handler: handler,
25666             scope: scope,
25667             minWidth: this.minButtonWidth,
25668             hideParent:true
25669         };
25670         if(typeof config == "string"){
25671             bc.text = config;
25672         }else{
25673             Roo.apply(bc, config);
25674         }
25675         var btn = new Roo.Button(null, bc);
25676         this.buttons.push(btn);
25677         return btn;
25678     },
25679
25680      /**
25681      * Adds a series of form elements (using the xtype property as the factory method.
25682      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25683      * @param {Object} config 
25684      */
25685     
25686     addxtype : function()
25687     {
25688         var ar = Array.prototype.slice.call(arguments, 0);
25689         var ret = false;
25690         for(var i = 0; i < ar.length; i++) {
25691             if (!ar[i]) {
25692                 continue; // skip -- if this happends something invalid got sent, we 
25693                 // should ignore it, as basically that interface element will not show up
25694                 // and that should be pretty obvious!!
25695             }
25696             
25697             if (Roo.form[ar[i].xtype]) {
25698                 ar[i].form = this;
25699                 var fe = Roo.factory(ar[i], Roo.form);
25700                 if (!ret) {
25701                     ret = fe;
25702                 }
25703                 fe.form = this;
25704                 if (fe.store) {
25705                     fe.store.form = this;
25706                 }
25707                 if (fe.isLayout) {  
25708                          
25709                     this.start(fe);
25710                     this.allItems.push(fe);
25711                     if (fe.items && fe.addxtype) {
25712                         fe.addxtype.apply(fe, fe.items);
25713                         delete fe.items;
25714                     }
25715                      this.end();
25716                     continue;
25717                 }
25718                 
25719                 
25720                  
25721                 this.add(fe);
25722               //  console.log('adding ' + ar[i].xtype);
25723             }
25724             if (ar[i].xtype == 'Button') {  
25725                 //console.log('adding button');
25726                 //console.log(ar[i]);
25727                 this.addButton(ar[i]);
25728                 this.allItems.push(fe);
25729                 continue;
25730             }
25731             
25732             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25733                 alert('end is not supported on xtype any more, use items');
25734             //    this.end();
25735             //    //console.log('adding end');
25736             }
25737             
25738         }
25739         return ret;
25740     },
25741     
25742     /**
25743      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
25744      * option "monitorValid"
25745      */
25746     startMonitoring : function(){
25747         if(!this.bound){
25748             this.bound = true;
25749             Roo.TaskMgr.start({
25750                 run : this.bindHandler,
25751                 interval : this.monitorPoll || 200,
25752                 scope: this
25753             });
25754         }
25755     },
25756
25757     /**
25758      * Stops monitoring of the valid state of this form
25759      */
25760     stopMonitoring : function(){
25761         this.bound = false;
25762     },
25763
25764     // private
25765     bindHandler : function(){
25766         if(!this.bound){
25767             return false; // stops binding
25768         }
25769         var valid = true;
25770         this.items.each(function(f){
25771             if(!f.isValid(true)){
25772                 valid = false;
25773                 return false;
25774             }
25775         });
25776         for(var i = 0, len = this.buttons.length; i < len; i++){
25777             var btn = this.buttons[i];
25778             if(btn.formBind === true && btn.disabled === valid){
25779                 btn.setDisabled(!valid);
25780             }
25781         }
25782         this.fireEvent('clientvalidation', this, valid);
25783     }
25784     
25785     
25786     
25787     
25788     
25789     
25790     
25791     
25792 });
25793
25794
25795 // back compat
25796 Roo.Form = Roo.form.Form;
25797 /*
25798  * Based on:
25799  * Ext JS Library 1.1.1
25800  * Copyright(c) 2006-2007, Ext JS, LLC.
25801  *
25802  * Originally Released Under LGPL - original licence link has changed is not relivant.
25803  *
25804  * Fork - LGPL
25805  * <script type="text/javascript">
25806  */
25807
25808 // as we use this in bootstrap.
25809 Roo.namespace('Roo.form');
25810  /**
25811  * @class Roo.form.Action
25812  * Internal Class used to handle form actions
25813  * @constructor
25814  * @param {Roo.form.BasicForm} el The form element or its id
25815  * @param {Object} config Configuration options
25816  */
25817
25818  
25819  
25820 // define the action interface
25821 Roo.form.Action = function(form, options){
25822     this.form = form;
25823     this.options = options || {};
25824 };
25825 /**
25826  * Client Validation Failed
25827  * @const 
25828  */
25829 Roo.form.Action.CLIENT_INVALID = 'client';
25830 /**
25831  * Server Validation Failed
25832  * @const 
25833  */
25834 Roo.form.Action.SERVER_INVALID = 'server';
25835  /**
25836  * Connect to Server Failed
25837  * @const 
25838  */
25839 Roo.form.Action.CONNECT_FAILURE = 'connect';
25840 /**
25841  * Reading Data from Server Failed
25842  * @const 
25843  */
25844 Roo.form.Action.LOAD_FAILURE = 'load';
25845
25846 Roo.form.Action.prototype = {
25847     type : 'default',
25848     failureType : undefined,
25849     response : undefined,
25850     result : undefined,
25851
25852     // interface method
25853     run : function(options){
25854
25855     },
25856
25857     // interface method
25858     success : function(response){
25859
25860     },
25861
25862     // interface method
25863     handleResponse : function(response){
25864
25865     },
25866
25867     // default connection failure
25868     failure : function(response){
25869         
25870         this.response = response;
25871         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25872         this.form.afterAction(this, false);
25873     },
25874
25875     processResponse : function(response){
25876         this.response = response;
25877         if(!response.responseText){
25878             return true;
25879         }
25880         this.result = this.handleResponse(response);
25881         return this.result;
25882     },
25883
25884     // utility functions used internally
25885     getUrl : function(appendParams){
25886         var url = this.options.url || this.form.url || this.form.el.dom.action;
25887         if(appendParams){
25888             var p = this.getParams();
25889             if(p){
25890                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
25891             }
25892         }
25893         return url;
25894     },
25895
25896     getMethod : function(){
25897         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
25898     },
25899
25900     getParams : function(){
25901         var bp = this.form.baseParams;
25902         var p = this.options.params;
25903         if(p){
25904             if(typeof p == "object"){
25905                 p = Roo.urlEncode(Roo.applyIf(p, bp));
25906             }else if(typeof p == 'string' && bp){
25907                 p += '&' + Roo.urlEncode(bp);
25908             }
25909         }else if(bp){
25910             p = Roo.urlEncode(bp);
25911         }
25912         return p;
25913     },
25914
25915     createCallback : function(){
25916         return {
25917             success: this.success,
25918             failure: this.failure,
25919             scope: this,
25920             timeout: (this.form.timeout*1000),
25921             upload: this.form.fileUpload ? this.success : undefined
25922         };
25923     }
25924 };
25925
25926 Roo.form.Action.Submit = function(form, options){
25927     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
25928 };
25929
25930 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
25931     type : 'submit',
25932
25933     haveProgress : false,
25934     uploadComplete : false,
25935     
25936     // uploadProgress indicator.
25937     uploadProgress : function()
25938     {
25939         if (!this.form.progressUrl) {
25940             return;
25941         }
25942         
25943         if (!this.haveProgress) {
25944             Roo.MessageBox.progress("Uploading", "Uploading");
25945         }
25946         if (this.uploadComplete) {
25947            Roo.MessageBox.hide();
25948            return;
25949         }
25950         
25951         this.haveProgress = true;
25952    
25953         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
25954         
25955         var c = new Roo.data.Connection();
25956         c.request({
25957             url : this.form.progressUrl,
25958             params: {
25959                 id : uid
25960             },
25961             method: 'GET',
25962             success : function(req){
25963                //console.log(data);
25964                 var rdata = false;
25965                 var edata;
25966                 try  {
25967                    rdata = Roo.decode(req.responseText)
25968                 } catch (e) {
25969                     Roo.log("Invalid data from server..");
25970                     Roo.log(edata);
25971                     return;
25972                 }
25973                 if (!rdata || !rdata.success) {
25974                     Roo.log(rdata);
25975                     Roo.MessageBox.alert(Roo.encode(rdata));
25976                     return;
25977                 }
25978                 var data = rdata.data;
25979                 
25980                 if (this.uploadComplete) {
25981                    Roo.MessageBox.hide();
25982                    return;
25983                 }
25984                    
25985                 if (data){
25986                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
25987                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
25988                     );
25989                 }
25990                 this.uploadProgress.defer(2000,this);
25991             },
25992        
25993             failure: function(data) {
25994                 Roo.log('progress url failed ');
25995                 Roo.log(data);
25996             },
25997             scope : this
25998         });
25999            
26000     },
26001     
26002     
26003     run : function()
26004     {
26005         // run get Values on the form, so it syncs any secondary forms.
26006         this.form.getValues();
26007         
26008         var o = this.options;
26009         var method = this.getMethod();
26010         var isPost = method == 'POST';
26011         if(o.clientValidation === false || this.form.isValid()){
26012             
26013             if (this.form.progressUrl) {
26014                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
26015                     (new Date() * 1) + '' + Math.random());
26016                     
26017             } 
26018             
26019             
26020             Roo.Ajax.request(Roo.apply(this.createCallback(), {
26021                 form:this.form.el.dom,
26022                 url:this.getUrl(!isPost),
26023                 method: method,
26024                 params:isPost ? this.getParams() : null,
26025                 isUpload: this.form.fileUpload,
26026                 formData : this.form.formData
26027             }));
26028             
26029             this.uploadProgress();
26030
26031         }else if (o.clientValidation !== false){ // client validation failed
26032             this.failureType = Roo.form.Action.CLIENT_INVALID;
26033             this.form.afterAction(this, false);
26034         }
26035     },
26036
26037     success : function(response)
26038     {
26039         this.uploadComplete= true;
26040         if (this.haveProgress) {
26041             Roo.MessageBox.hide();
26042         }
26043         
26044         
26045         var result = this.processResponse(response);
26046         if(result === true || result.success){
26047             this.form.afterAction(this, true);
26048             return;
26049         }
26050         if(result.errors){
26051             this.form.markInvalid(result.errors);
26052             this.failureType = Roo.form.Action.SERVER_INVALID;
26053         }
26054         this.form.afterAction(this, false);
26055     },
26056     failure : function(response)
26057     {
26058         this.uploadComplete= true;
26059         if (this.haveProgress) {
26060             Roo.MessageBox.hide();
26061         }
26062         
26063         this.response = response;
26064         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26065         this.form.afterAction(this, false);
26066     },
26067     
26068     handleResponse : function(response){
26069         if(this.form.errorReader){
26070             var rs = this.form.errorReader.read(response);
26071             var errors = [];
26072             if(rs.records){
26073                 for(var i = 0, len = rs.records.length; i < len; i++) {
26074                     var r = rs.records[i];
26075                     errors[i] = r.data;
26076                 }
26077             }
26078             if(errors.length < 1){
26079                 errors = null;
26080             }
26081             return {
26082                 success : rs.success,
26083                 errors : errors
26084             };
26085         }
26086         var ret = false;
26087         try {
26088             ret = Roo.decode(response.responseText);
26089         } catch (e) {
26090             ret = {
26091                 success: false,
26092                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
26093                 errors : []
26094             };
26095         }
26096         return ret;
26097         
26098     }
26099 });
26100
26101
26102 Roo.form.Action.Load = function(form, options){
26103     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
26104     this.reader = this.form.reader;
26105 };
26106
26107 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
26108     type : 'load',
26109
26110     run : function(){
26111         
26112         Roo.Ajax.request(Roo.apply(
26113                 this.createCallback(), {
26114                     method:this.getMethod(),
26115                     url:this.getUrl(false),
26116                     params:this.getParams()
26117         }));
26118     },
26119
26120     success : function(response){
26121         
26122         var result = this.processResponse(response);
26123         if(result === true || !result.success || !result.data){
26124             this.failureType = Roo.form.Action.LOAD_FAILURE;
26125             this.form.afterAction(this, false);
26126             return;
26127         }
26128         this.form.clearInvalid();
26129         this.form.setValues(result.data);
26130         this.form.afterAction(this, true);
26131     },
26132
26133     handleResponse : function(response){
26134         if(this.form.reader){
26135             var rs = this.form.reader.read(response);
26136             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
26137             return {
26138                 success : rs.success,
26139                 data : data
26140             };
26141         }
26142         return Roo.decode(response.responseText);
26143     }
26144 });
26145
26146 Roo.form.Action.ACTION_TYPES = {
26147     'load' : Roo.form.Action.Load,
26148     'submit' : Roo.form.Action.Submit
26149 };/*
26150  * Based on:
26151  * Ext JS Library 1.1.1
26152  * Copyright(c) 2006-2007, Ext JS, LLC.
26153  *
26154  * Originally Released Under LGPL - original licence link has changed is not relivant.
26155  *
26156  * Fork - LGPL
26157  * <script type="text/javascript">
26158  */
26159  
26160 /**
26161  * @class Roo.form.Layout
26162  * @extends Roo.Component
26163  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26164  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
26165  * @constructor
26166  * @param {Object} config Configuration options
26167  */
26168 Roo.form.Layout = function(config){
26169     var xitems = [];
26170     if (config.items) {
26171         xitems = config.items;
26172         delete config.items;
26173     }
26174     Roo.form.Layout.superclass.constructor.call(this, config);
26175     this.stack = [];
26176     Roo.each(xitems, this.addxtype, this);
26177      
26178 };
26179
26180 Roo.extend(Roo.form.Layout, Roo.Component, {
26181     /**
26182      * @cfg {String/Object} autoCreate
26183      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
26184      */
26185     /**
26186      * @cfg {String/Object/Function} style
26187      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
26188      * a function which returns such a specification.
26189      */
26190     /**
26191      * @cfg {String} labelAlign
26192      * Valid values are "left," "top" and "right" (defaults to "left")
26193      */
26194     /**
26195      * @cfg {Number} labelWidth
26196      * Fixed width in pixels of all field labels (defaults to undefined)
26197      */
26198     /**
26199      * @cfg {Boolean} clear
26200      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
26201      */
26202     clear : true,
26203     /**
26204      * @cfg {String} labelSeparator
26205      * The separator to use after field labels (defaults to ':')
26206      */
26207     labelSeparator : ':',
26208     /**
26209      * @cfg {Boolean} hideLabels
26210      * True to suppress the display of field labels in this layout (defaults to false)
26211      */
26212     hideLabels : false,
26213
26214     // private
26215     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
26216     
26217     isLayout : true,
26218     
26219     // private
26220     onRender : function(ct, position){
26221         if(this.el){ // from markup
26222             this.el = Roo.get(this.el);
26223         }else {  // generate
26224             var cfg = this.getAutoCreate();
26225             this.el = ct.createChild(cfg, position);
26226         }
26227         if(this.style){
26228             this.el.applyStyles(this.style);
26229         }
26230         if(this.labelAlign){
26231             this.el.addClass('x-form-label-'+this.labelAlign);
26232         }
26233         if(this.hideLabels){
26234             this.labelStyle = "display:none";
26235             this.elementStyle = "padding-left:0;";
26236         }else{
26237             if(typeof this.labelWidth == 'number'){
26238                 this.labelStyle = "width:"+this.labelWidth+"px;";
26239                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
26240             }
26241             if(this.labelAlign == 'top'){
26242                 this.labelStyle = "width:auto;";
26243                 this.elementStyle = "padding-left:0;";
26244             }
26245         }
26246         var stack = this.stack;
26247         var slen = stack.length;
26248         if(slen > 0){
26249             if(!this.fieldTpl){
26250                 var t = new Roo.Template(
26251                     '<div class="x-form-item {5}">',
26252                         '<label for="{0}" style="{2}">{1}{4}</label>',
26253                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26254                         '</div>',
26255                     '</div><div class="x-form-clear-left"></div>'
26256                 );
26257                 t.disableFormats = true;
26258                 t.compile();
26259                 Roo.form.Layout.prototype.fieldTpl = t;
26260             }
26261             for(var i = 0; i < slen; i++) {
26262                 if(stack[i].isFormField){
26263                     this.renderField(stack[i]);
26264                 }else{
26265                     this.renderComponent(stack[i]);
26266                 }
26267             }
26268         }
26269         if(this.clear){
26270             this.el.createChild({cls:'x-form-clear'});
26271         }
26272     },
26273
26274     // private
26275     renderField : function(f){
26276         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
26277                f.id, //0
26278                f.fieldLabel, //1
26279                f.labelStyle||this.labelStyle||'', //2
26280                this.elementStyle||'', //3
26281                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
26282                f.itemCls||this.itemCls||''  //5
26283        ], true).getPrevSibling());
26284     },
26285
26286     // private
26287     renderComponent : function(c){
26288         c.render(c.isLayout ? this.el : this.el.createChild());    
26289     },
26290     /**
26291      * Adds a object form elements (using the xtype property as the factory method.)
26292      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
26293      * @param {Object} config 
26294      */
26295     addxtype : function(o)
26296     {
26297         // create the lement.
26298         o.form = this.form;
26299         var fe = Roo.factory(o, Roo.form);
26300         this.form.allItems.push(fe);
26301         this.stack.push(fe);
26302         
26303         if (fe.isFormField) {
26304             this.form.items.add(fe);
26305         }
26306          
26307         return fe;
26308     }
26309 });
26310
26311 /**
26312  * @class Roo.form.Column
26313  * @extends Roo.form.Layout
26314  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
26315  * @constructor
26316  * @param {Object} config Configuration options
26317  */
26318 Roo.form.Column = function(config){
26319     Roo.form.Column.superclass.constructor.call(this, config);
26320 };
26321
26322 Roo.extend(Roo.form.Column, Roo.form.Layout, {
26323     /**
26324      * @cfg {Number/String} width
26325      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26326      */
26327     /**
26328      * @cfg {String/Object} autoCreate
26329      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
26330      */
26331
26332     // private
26333     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
26334
26335     // private
26336     onRender : function(ct, position){
26337         Roo.form.Column.superclass.onRender.call(this, ct, position);
26338         if(this.width){
26339             this.el.setWidth(this.width);
26340         }
26341     }
26342 });
26343
26344
26345 /**
26346  * @class Roo.form.Row
26347  * @extends Roo.form.Layout
26348  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26349  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
26350  * @constructor
26351  * @param {Object} config Configuration options
26352  */
26353
26354  
26355 Roo.form.Row = function(config){
26356     Roo.form.Row.superclass.constructor.call(this, config);
26357 };
26358  
26359 Roo.extend(Roo.form.Row, Roo.form.Layout, {
26360       /**
26361      * @cfg {Number/String} width
26362      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26363      */
26364     /**
26365      * @cfg {Number/String} height
26366      * The fixed height of the column in pixels or CSS value (defaults to "auto")
26367      */
26368     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
26369     
26370     padWidth : 20,
26371     // private
26372     onRender : function(ct, position){
26373         //console.log('row render');
26374         if(!this.rowTpl){
26375             var t = new Roo.Template(
26376                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
26377                     '<label for="{0}" style="{2}">{1}{4}</label>',
26378                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26379                     '</div>',
26380                 '</div>'
26381             );
26382             t.disableFormats = true;
26383             t.compile();
26384             Roo.form.Layout.prototype.rowTpl = t;
26385         }
26386         this.fieldTpl = this.rowTpl;
26387         
26388         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26389         var labelWidth = 100;
26390         
26391         if ((this.labelAlign != 'top')) {
26392             if (typeof this.labelWidth == 'number') {
26393                 labelWidth = this.labelWidth
26394             }
26395             this.padWidth =  20 + labelWidth;
26396             
26397         }
26398         
26399         Roo.form.Column.superclass.onRender.call(this, ct, position);
26400         if(this.width){
26401             this.el.setWidth(this.width);
26402         }
26403         if(this.height){
26404             this.el.setHeight(this.height);
26405         }
26406     },
26407     
26408     // private
26409     renderField : function(f){
26410         f.fieldEl = this.fieldTpl.append(this.el, [
26411                f.id, f.fieldLabel,
26412                f.labelStyle||this.labelStyle||'',
26413                this.elementStyle||'',
26414                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26415                f.itemCls||this.itemCls||'',
26416                f.width ? f.width + this.padWidth : 160 + this.padWidth
26417        ],true);
26418     }
26419 });
26420  
26421
26422 /**
26423  * @class Roo.form.FieldSet
26424  * @extends Roo.form.Layout
26425  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26426  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26427  * @constructor
26428  * @param {Object} config Configuration options
26429  */
26430 Roo.form.FieldSet = function(config){
26431     Roo.form.FieldSet.superclass.constructor.call(this, config);
26432 };
26433
26434 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26435     /**
26436      * @cfg {String} legend
26437      * The text to display as the legend for the FieldSet (defaults to '')
26438      */
26439     /**
26440      * @cfg {String/Object} autoCreate
26441      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26442      */
26443
26444     // private
26445     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26446
26447     // private
26448     onRender : function(ct, position){
26449         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26450         if(this.legend){
26451             this.setLegend(this.legend);
26452         }
26453     },
26454
26455     // private
26456     setLegend : function(text){
26457         if(this.rendered){
26458             this.el.child('legend').update(text);
26459         }
26460     }
26461 });/*
26462  * Based on:
26463  * Ext JS Library 1.1.1
26464  * Copyright(c) 2006-2007, Ext JS, LLC.
26465  *
26466  * Originally Released Under LGPL - original licence link has changed is not relivant.
26467  *
26468  * Fork - LGPL
26469  * <script type="text/javascript">
26470  */
26471 /**
26472  * @class Roo.form.VTypes
26473  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26474  * @singleton
26475  */
26476 Roo.form.VTypes = function(){
26477     // closure these in so they are only created once.
26478     var alpha = /^[a-zA-Z_]+$/;
26479     var alphanum = /^[a-zA-Z0-9_]+$/;
26480     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26481     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26482
26483     // All these messages and functions are configurable
26484     return {
26485         /**
26486          * The function used to validate email addresses
26487          * @param {String} value The email address
26488          */
26489         'email' : function(v){
26490             return email.test(v);
26491         },
26492         /**
26493          * The error text to display when the email validation function returns false
26494          * @type String
26495          */
26496         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26497         /**
26498          * The keystroke filter mask to be applied on email input
26499          * @type RegExp
26500          */
26501         'emailMask' : /[a-z0-9_\.\-@]/i,
26502
26503         /**
26504          * The function used to validate URLs
26505          * @param {String} value The URL
26506          */
26507         'url' : function(v){
26508             return url.test(v);
26509         },
26510         /**
26511          * The error text to display when the url validation function returns false
26512          * @type String
26513          */
26514         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26515         
26516         /**
26517          * The function used to validate alpha values
26518          * @param {String} value The value
26519          */
26520         'alpha' : function(v){
26521             return alpha.test(v);
26522         },
26523         /**
26524          * The error text to display when the alpha validation function returns false
26525          * @type String
26526          */
26527         'alphaText' : 'This field should only contain letters and _',
26528         /**
26529          * The keystroke filter mask to be applied on alpha input
26530          * @type RegExp
26531          */
26532         'alphaMask' : /[a-z_]/i,
26533
26534         /**
26535          * The function used to validate alphanumeric values
26536          * @param {String} value The value
26537          */
26538         'alphanum' : function(v){
26539             return alphanum.test(v);
26540         },
26541         /**
26542          * The error text to display when the alphanumeric validation function returns false
26543          * @type String
26544          */
26545         'alphanumText' : 'This field should only contain letters, numbers and _',
26546         /**
26547          * The keystroke filter mask to be applied on alphanumeric input
26548          * @type RegExp
26549          */
26550         'alphanumMask' : /[a-z0-9_]/i
26551     };
26552 }();//<script type="text/javascript">
26553
26554 /**
26555  * @class Roo.form.FCKeditor
26556  * @extends Roo.form.TextArea
26557  * Wrapper around the FCKEditor http://www.fckeditor.net
26558  * @constructor
26559  * Creates a new FCKeditor
26560  * @param {Object} config Configuration options
26561  */
26562 Roo.form.FCKeditor = function(config){
26563     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26564     this.addEvents({
26565          /**
26566          * @event editorinit
26567          * Fired when the editor is initialized - you can add extra handlers here..
26568          * @param {FCKeditor} this
26569          * @param {Object} the FCK object.
26570          */
26571         editorinit : true
26572     });
26573     
26574     
26575 };
26576 Roo.form.FCKeditor.editors = { };
26577 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26578 {
26579     //defaultAutoCreate : {
26580     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26581     //},
26582     // private
26583     /**
26584      * @cfg {Object} fck options - see fck manual for details.
26585      */
26586     fckconfig : false,
26587     
26588     /**
26589      * @cfg {Object} fck toolbar set (Basic or Default)
26590      */
26591     toolbarSet : 'Basic',
26592     /**
26593      * @cfg {Object} fck BasePath
26594      */ 
26595     basePath : '/fckeditor/',
26596     
26597     
26598     frame : false,
26599     
26600     value : '',
26601     
26602    
26603     onRender : function(ct, position)
26604     {
26605         if(!this.el){
26606             this.defaultAutoCreate = {
26607                 tag: "textarea",
26608                 style:"width:300px;height:60px;",
26609                 autocomplete: "new-password"
26610             };
26611         }
26612         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26613         /*
26614         if(this.grow){
26615             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26616             if(this.preventScrollbars){
26617                 this.el.setStyle("overflow", "hidden");
26618             }
26619             this.el.setHeight(this.growMin);
26620         }
26621         */
26622         //console.log('onrender' + this.getId() );
26623         Roo.form.FCKeditor.editors[this.getId()] = this;
26624          
26625
26626         this.replaceTextarea() ;
26627         
26628     },
26629     
26630     getEditor : function() {
26631         return this.fckEditor;
26632     },
26633     /**
26634      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26635      * @param {Mixed} value The value to set
26636      */
26637     
26638     
26639     setValue : function(value)
26640     {
26641         //console.log('setValue: ' + value);
26642         
26643         if(typeof(value) == 'undefined') { // not sure why this is happending...
26644             return;
26645         }
26646         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26647         
26648         //if(!this.el || !this.getEditor()) {
26649         //    this.value = value;
26650             //this.setValue.defer(100,this,[value]);    
26651         //    return;
26652         //} 
26653         
26654         if(!this.getEditor()) {
26655             return;
26656         }
26657         
26658         this.getEditor().SetData(value);
26659         
26660         //
26661
26662     },
26663
26664     /**
26665      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26666      * @return {Mixed} value The field value
26667      */
26668     getValue : function()
26669     {
26670         
26671         if (this.frame && this.frame.dom.style.display == 'none') {
26672             return Roo.form.FCKeditor.superclass.getValue.call(this);
26673         }
26674         
26675         if(!this.el || !this.getEditor()) {
26676            
26677            // this.getValue.defer(100,this); 
26678             return this.value;
26679         }
26680        
26681         
26682         var value=this.getEditor().GetData();
26683         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26684         return Roo.form.FCKeditor.superclass.getValue.call(this);
26685         
26686
26687     },
26688
26689     /**
26690      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26691      * @return {Mixed} value The field value
26692      */
26693     getRawValue : function()
26694     {
26695         if (this.frame && this.frame.dom.style.display == 'none') {
26696             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26697         }
26698         
26699         if(!this.el || !this.getEditor()) {
26700             //this.getRawValue.defer(100,this); 
26701             return this.value;
26702             return;
26703         }
26704         
26705         
26706         
26707         var value=this.getEditor().GetData();
26708         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26709         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26710          
26711     },
26712     
26713     setSize : function(w,h) {
26714         
26715         
26716         
26717         //if (this.frame && this.frame.dom.style.display == 'none') {
26718         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26719         //    return;
26720         //}
26721         //if(!this.el || !this.getEditor()) {
26722         //    this.setSize.defer(100,this, [w,h]); 
26723         //    return;
26724         //}
26725         
26726         
26727         
26728         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26729         
26730         this.frame.dom.setAttribute('width', w);
26731         this.frame.dom.setAttribute('height', h);
26732         this.frame.setSize(w,h);
26733         
26734     },
26735     
26736     toggleSourceEdit : function(value) {
26737         
26738       
26739          
26740         this.el.dom.style.display = value ? '' : 'none';
26741         this.frame.dom.style.display = value ?  'none' : '';
26742         
26743     },
26744     
26745     
26746     focus: function(tag)
26747     {
26748         if (this.frame.dom.style.display == 'none') {
26749             return Roo.form.FCKeditor.superclass.focus.call(this);
26750         }
26751         if(!this.el || !this.getEditor()) {
26752             this.focus.defer(100,this, [tag]); 
26753             return;
26754         }
26755         
26756         
26757         
26758         
26759         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
26760         this.getEditor().Focus();
26761         if (tgs.length) {
26762             if (!this.getEditor().Selection.GetSelection()) {
26763                 this.focus.defer(100,this, [tag]); 
26764                 return;
26765             }
26766             
26767             
26768             var r = this.getEditor().EditorDocument.createRange();
26769             r.setStart(tgs[0],0);
26770             r.setEnd(tgs[0],0);
26771             this.getEditor().Selection.GetSelection().removeAllRanges();
26772             this.getEditor().Selection.GetSelection().addRange(r);
26773             this.getEditor().Focus();
26774         }
26775         
26776     },
26777     
26778     
26779     
26780     replaceTextarea : function()
26781     {
26782         if ( document.getElementById( this.getId() + '___Frame' ) ) {
26783             return ;
26784         }
26785         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
26786         //{
26787             // We must check the elements firstly using the Id and then the name.
26788         var oTextarea = document.getElementById( this.getId() );
26789         
26790         var colElementsByName = document.getElementsByName( this.getId() ) ;
26791          
26792         oTextarea.style.display = 'none' ;
26793
26794         if ( oTextarea.tabIndex ) {            
26795             this.TabIndex = oTextarea.tabIndex ;
26796         }
26797         
26798         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
26799         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
26800         this.frame = Roo.get(this.getId() + '___Frame')
26801     },
26802     
26803     _getConfigHtml : function()
26804     {
26805         var sConfig = '' ;
26806
26807         for ( var o in this.fckconfig ) {
26808             sConfig += sConfig.length > 0  ? '&amp;' : '';
26809             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
26810         }
26811
26812         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
26813     },
26814     
26815     
26816     _getIFrameHtml : function()
26817     {
26818         var sFile = 'fckeditor.html' ;
26819         /* no idea what this is about..
26820         try
26821         {
26822             if ( (/fcksource=true/i).test( window.top.location.search ) )
26823                 sFile = 'fckeditor.original.html' ;
26824         }
26825         catch (e) { 
26826         */
26827
26828         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
26829         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
26830         
26831         
26832         var html = '<iframe id="' + this.getId() +
26833             '___Frame" src="' + sLink +
26834             '" width="' + this.width +
26835             '" height="' + this.height + '"' +
26836             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
26837             ' frameborder="0" scrolling="no"></iframe>' ;
26838
26839         return html ;
26840     },
26841     
26842     _insertHtmlBefore : function( html, element )
26843     {
26844         if ( element.insertAdjacentHTML )       {
26845             // IE
26846             element.insertAdjacentHTML( 'beforeBegin', html ) ;
26847         } else { // Gecko
26848             var oRange = document.createRange() ;
26849             oRange.setStartBefore( element ) ;
26850             var oFragment = oRange.createContextualFragment( html );
26851             element.parentNode.insertBefore( oFragment, element ) ;
26852         }
26853     }
26854     
26855     
26856   
26857     
26858     
26859     
26860     
26861
26862 });
26863
26864 //Roo.reg('fckeditor', Roo.form.FCKeditor);
26865
26866 function FCKeditor_OnComplete(editorInstance){
26867     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
26868     f.fckEditor = editorInstance;
26869     //console.log("loaded");
26870     f.fireEvent('editorinit', f, editorInstance);
26871
26872   
26873
26874  
26875
26876
26877
26878
26879
26880
26881
26882
26883
26884
26885
26886
26887
26888
26889
26890 //<script type="text/javascript">
26891 /**
26892  * @class Roo.form.GridField
26893  * @extends Roo.form.Field
26894  * Embed a grid (or editable grid into a form)
26895  * STATUS ALPHA
26896  * 
26897  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
26898  * it needs 
26899  * xgrid.store = Roo.data.Store
26900  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
26901  * xgrid.store.reader = Roo.data.JsonReader 
26902  * 
26903  * 
26904  * @constructor
26905  * Creates a new GridField
26906  * @param {Object} config Configuration options
26907  */
26908 Roo.form.GridField = function(config){
26909     Roo.form.GridField.superclass.constructor.call(this, config);
26910      
26911 };
26912
26913 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
26914     /**
26915      * @cfg {Number} width  - used to restrict width of grid..
26916      */
26917     width : 100,
26918     /**
26919      * @cfg {Number} height - used to restrict height of grid..
26920      */
26921     height : 50,
26922      /**
26923      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
26924          * 
26925          *}
26926      */
26927     xgrid : false, 
26928     /**
26929      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26930      * {tag: "input", type: "checkbox", autocomplete: "off"})
26931      */
26932    // defaultAutoCreate : { tag: 'div' },
26933     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
26934     /**
26935      * @cfg {String} addTitle Text to include for adding a title.
26936      */
26937     addTitle : false,
26938     //
26939     onResize : function(){
26940         Roo.form.Field.superclass.onResize.apply(this, arguments);
26941     },
26942
26943     initEvents : function(){
26944         // Roo.form.Checkbox.superclass.initEvents.call(this);
26945         // has no events...
26946        
26947     },
26948
26949
26950     getResizeEl : function(){
26951         return this.wrap;
26952     },
26953
26954     getPositionEl : function(){
26955         return this.wrap;
26956     },
26957
26958     // private
26959     onRender : function(ct, position){
26960         
26961         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
26962         var style = this.style;
26963         delete this.style;
26964         
26965         Roo.form.GridField.superclass.onRender.call(this, ct, position);
26966         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
26967         this.viewEl = this.wrap.createChild({ tag: 'div' });
26968         if (style) {
26969             this.viewEl.applyStyles(style);
26970         }
26971         if (this.width) {
26972             this.viewEl.setWidth(this.width);
26973         }
26974         if (this.height) {
26975             this.viewEl.setHeight(this.height);
26976         }
26977         //if(this.inputValue !== undefined){
26978         //this.setValue(this.value);
26979         
26980         
26981         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
26982         
26983         
26984         this.grid.render();
26985         this.grid.getDataSource().on('remove', this.refreshValue, this);
26986         this.grid.getDataSource().on('update', this.refreshValue, this);
26987         this.grid.on('afteredit', this.refreshValue, this);
26988  
26989     },
26990      
26991     
26992     /**
26993      * Sets the value of the item. 
26994      * @param {String} either an object  or a string..
26995      */
26996     setValue : function(v){
26997         //this.value = v;
26998         v = v || []; // empty set..
26999         // this does not seem smart - it really only affects memoryproxy grids..
27000         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
27001             var ds = this.grid.getDataSource();
27002             // assumes a json reader..
27003             var data = {}
27004             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
27005             ds.loadData( data);
27006         }
27007         // clear selection so it does not get stale.
27008         if (this.grid.sm) { 
27009             this.grid.sm.clearSelections();
27010         }
27011         
27012         Roo.form.GridField.superclass.setValue.call(this, v);
27013         this.refreshValue();
27014         // should load data in the grid really....
27015     },
27016     
27017     // private
27018     refreshValue: function() {
27019          var val = [];
27020         this.grid.getDataSource().each(function(r) {
27021             val.push(r.data);
27022         });
27023         this.el.dom.value = Roo.encode(val);
27024     }
27025     
27026      
27027     
27028     
27029 });/*
27030  * Based on:
27031  * Ext JS Library 1.1.1
27032  * Copyright(c) 2006-2007, Ext JS, LLC.
27033  *
27034  * Originally Released Under LGPL - original licence link has changed is not relivant.
27035  *
27036  * Fork - LGPL
27037  * <script type="text/javascript">
27038  */
27039 /**
27040  * @class Roo.form.DisplayField
27041  * @extends Roo.form.Field
27042  * A generic Field to display non-editable data.
27043  * @cfg {Boolean} closable (true|false) default false
27044  * @constructor
27045  * Creates a new Display Field item.
27046  * @param {Object} config Configuration options
27047  */
27048 Roo.form.DisplayField = function(config){
27049     Roo.form.DisplayField.superclass.constructor.call(this, config);
27050     
27051     this.addEvents({
27052         /**
27053          * @event close
27054          * Fires after the click the close btn
27055              * @param {Roo.form.DisplayField} this
27056              */
27057         close : true
27058     });
27059 };
27060
27061 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
27062     inputType:      'hidden',
27063     allowBlank:     true,
27064     readOnly:         true,
27065     
27066  
27067     /**
27068      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27069      */
27070     focusClass : undefined,
27071     /**
27072      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27073      */
27074     fieldClass: 'x-form-field',
27075     
27076      /**
27077      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
27078      */
27079     valueRenderer: undefined,
27080     
27081     width: 100,
27082     /**
27083      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27084      * {tag: "input", type: "checkbox", autocomplete: "off"})
27085      */
27086      
27087  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27088  
27089     closable : false,
27090     
27091     onResize : function(){
27092         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
27093         
27094     },
27095
27096     initEvents : function(){
27097         // Roo.form.Checkbox.superclass.initEvents.call(this);
27098         // has no events...
27099         
27100         if(this.closable){
27101             this.closeEl.on('click', this.onClose, this);
27102         }
27103        
27104     },
27105
27106
27107     getResizeEl : function(){
27108         return this.wrap;
27109     },
27110
27111     getPositionEl : function(){
27112         return this.wrap;
27113     },
27114
27115     // private
27116     onRender : function(ct, position){
27117         
27118         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
27119         //if(this.inputValue !== undefined){
27120         this.wrap = this.el.wrap();
27121         
27122         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
27123         
27124         if(this.closable){
27125             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
27126         }
27127         
27128         if (this.bodyStyle) {
27129             this.viewEl.applyStyles(this.bodyStyle);
27130         }
27131         //this.viewEl.setStyle('padding', '2px');
27132         
27133         this.setValue(this.value);
27134         
27135     },
27136 /*
27137     // private
27138     initValue : Roo.emptyFn,
27139
27140   */
27141
27142         // private
27143     onClick : function(){
27144         
27145     },
27146
27147     /**
27148      * Sets the checked state of the checkbox.
27149      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
27150      */
27151     setValue : function(v){
27152         this.value = v;
27153         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
27154         // this might be called before we have a dom element..
27155         if (!this.viewEl) {
27156             return;
27157         }
27158         this.viewEl.dom.innerHTML = html;
27159         Roo.form.DisplayField.superclass.setValue.call(this, v);
27160
27161     },
27162     
27163     onClose : function(e)
27164     {
27165         e.preventDefault();
27166         
27167         this.fireEvent('close', this);
27168     }
27169 });/*
27170  * 
27171  * Licence- LGPL
27172  * 
27173  */
27174
27175 /**
27176  * @class Roo.form.DayPicker
27177  * @extends Roo.form.Field
27178  * A Day picker show [M] [T] [W] ....
27179  * @constructor
27180  * Creates a new Day Picker
27181  * @param {Object} config Configuration options
27182  */
27183 Roo.form.DayPicker= function(config){
27184     Roo.form.DayPicker.superclass.constructor.call(this, config);
27185      
27186 };
27187
27188 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
27189     /**
27190      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27191      */
27192     focusClass : undefined,
27193     /**
27194      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27195      */
27196     fieldClass: "x-form-field",
27197    
27198     /**
27199      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27200      * {tag: "input", type: "checkbox", autocomplete: "off"})
27201      */
27202     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
27203     
27204    
27205     actionMode : 'viewEl', 
27206     //
27207     // private
27208  
27209     inputType : 'hidden',
27210     
27211      
27212     inputElement: false, // real input element?
27213     basedOn: false, // ????
27214     
27215     isFormField: true, // not sure where this is needed!!!!
27216
27217     onResize : function(){
27218         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
27219         if(!this.boxLabel){
27220             this.el.alignTo(this.wrap, 'c-c');
27221         }
27222     },
27223
27224     initEvents : function(){
27225         Roo.form.Checkbox.superclass.initEvents.call(this);
27226         this.el.on("click", this.onClick,  this);
27227         this.el.on("change", this.onClick,  this);
27228     },
27229
27230
27231     getResizeEl : function(){
27232         return this.wrap;
27233     },
27234
27235     getPositionEl : function(){
27236         return this.wrap;
27237     },
27238
27239     
27240     // private
27241     onRender : function(ct, position){
27242         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
27243        
27244         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
27245         
27246         var r1 = '<table><tr>';
27247         var r2 = '<tr class="x-form-daypick-icons">';
27248         for (var i=0; i < 7; i++) {
27249             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
27250             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
27251         }
27252         
27253         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
27254         viewEl.select('img').on('click', this.onClick, this);
27255         this.viewEl = viewEl;   
27256         
27257         
27258         // this will not work on Chrome!!!
27259         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
27260         this.el.on('propertychange', this.setFromHidden,  this);  //ie
27261         
27262         
27263           
27264
27265     },
27266
27267     // private
27268     initValue : Roo.emptyFn,
27269
27270     /**
27271      * Returns the checked state of the checkbox.
27272      * @return {Boolean} True if checked, else false
27273      */
27274     getValue : function(){
27275         return this.el.dom.value;
27276         
27277     },
27278
27279         // private
27280     onClick : function(e){ 
27281         //this.setChecked(!this.checked);
27282         Roo.get(e.target).toggleClass('x-menu-item-checked');
27283         this.refreshValue();
27284         //if(this.el.dom.checked != this.checked){
27285         //    this.setValue(this.el.dom.checked);
27286        // }
27287     },
27288     
27289     // private
27290     refreshValue : function()
27291     {
27292         var val = '';
27293         this.viewEl.select('img',true).each(function(e,i,n)  {
27294             val += e.is(".x-menu-item-checked") ? String(n) : '';
27295         });
27296         this.setValue(val, true);
27297     },
27298
27299     /**
27300      * Sets the checked state of the checkbox.
27301      * On is always based on a string comparison between inputValue and the param.
27302      * @param {Boolean/String} value - the value to set 
27303      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
27304      */
27305     setValue : function(v,suppressEvent){
27306         if (!this.el.dom) {
27307             return;
27308         }
27309         var old = this.el.dom.value ;
27310         this.el.dom.value = v;
27311         if (suppressEvent) {
27312             return ;
27313         }
27314          
27315         // update display..
27316         this.viewEl.select('img',true).each(function(e,i,n)  {
27317             
27318             var on = e.is(".x-menu-item-checked");
27319             var newv = v.indexOf(String(n)) > -1;
27320             if (on != newv) {
27321                 e.toggleClass('x-menu-item-checked');
27322             }
27323             
27324         });
27325         
27326         
27327         this.fireEvent('change', this, v, old);
27328         
27329         
27330     },
27331    
27332     // handle setting of hidden value by some other method!!?!?
27333     setFromHidden: function()
27334     {
27335         if(!this.el){
27336             return;
27337         }
27338         //console.log("SET FROM HIDDEN");
27339         //alert('setFrom hidden');
27340         this.setValue(this.el.dom.value);
27341     },
27342     
27343     onDestroy : function()
27344     {
27345         if(this.viewEl){
27346             Roo.get(this.viewEl).remove();
27347         }
27348          
27349         Roo.form.DayPicker.superclass.onDestroy.call(this);
27350     }
27351
27352 });/*
27353  * RooJS Library 1.1.1
27354  * Copyright(c) 2008-2011  Alan Knowles
27355  *
27356  * License - LGPL
27357  */
27358  
27359
27360 /**
27361  * @class Roo.form.ComboCheck
27362  * @extends Roo.form.ComboBox
27363  * A combobox for multiple select items.
27364  *
27365  * FIXME - could do with a reset button..
27366  * 
27367  * @constructor
27368  * Create a new ComboCheck
27369  * @param {Object} config Configuration options
27370  */
27371 Roo.form.ComboCheck = function(config){
27372     Roo.form.ComboCheck.superclass.constructor.call(this, config);
27373     // should verify some data...
27374     // like
27375     // hiddenName = required..
27376     // displayField = required
27377     // valudField == required
27378     var req= [ 'hiddenName', 'displayField', 'valueField' ];
27379     var _t = this;
27380     Roo.each(req, function(e) {
27381         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
27382             throw "Roo.form.ComboCheck : missing value for: " + e;
27383         }
27384     });
27385     
27386     
27387 };
27388
27389 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
27390      
27391      
27392     editable : false,
27393      
27394     selectedClass: 'x-menu-item-checked', 
27395     
27396     // private
27397     onRender : function(ct, position){
27398         var _t = this;
27399         
27400         
27401         
27402         if(!this.tpl){
27403             var cls = 'x-combo-list';
27404
27405             
27406             this.tpl =  new Roo.Template({
27407                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
27408                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
27409                    '<span>{' + this.displayField + '}</span>' +
27410                     '</div>' 
27411                 
27412             });
27413         }
27414  
27415         
27416         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
27417         this.view.singleSelect = false;
27418         this.view.multiSelect = true;
27419         this.view.toggleSelect = true;
27420         this.pageTb.add(new Roo.Toolbar.Fill(), {
27421             
27422             text: 'Done',
27423             handler: function()
27424             {
27425                 _t.collapse();
27426             }
27427         });
27428     },
27429     
27430     onViewOver : function(e, t){
27431         // do nothing...
27432         return;
27433         
27434     },
27435     
27436     onViewClick : function(doFocus,index){
27437         return;
27438         
27439     },
27440     select: function () {
27441         //Roo.log("SELECT CALLED");
27442     },
27443      
27444     selectByValue : function(xv, scrollIntoView){
27445         var ar = this.getValueArray();
27446         var sels = [];
27447         
27448         Roo.each(ar, function(v) {
27449             if(v === undefined || v === null){
27450                 return;
27451             }
27452             var r = this.findRecord(this.valueField, v);
27453             if(r){
27454                 sels.push(this.store.indexOf(r))
27455                 
27456             }
27457         },this);
27458         this.view.select(sels);
27459         return false;
27460     },
27461     
27462     
27463     
27464     onSelect : function(record, index){
27465        // Roo.log("onselect Called");
27466        // this is only called by the clear button now..
27467         this.view.clearSelections();
27468         this.setValue('[]');
27469         if (this.value != this.valueBefore) {
27470             this.fireEvent('change', this, this.value, this.valueBefore);
27471             this.valueBefore = this.value;
27472         }
27473     },
27474     getValueArray : function()
27475     {
27476         var ar = [] ;
27477         
27478         try {
27479             //Roo.log(this.value);
27480             if (typeof(this.value) == 'undefined') {
27481                 return [];
27482             }
27483             var ar = Roo.decode(this.value);
27484             return  ar instanceof Array ? ar : []; //?? valid?
27485             
27486         } catch(e) {
27487             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
27488             return [];
27489         }
27490          
27491     },
27492     expand : function ()
27493     {
27494         
27495         Roo.form.ComboCheck.superclass.expand.call(this);
27496         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27497         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27498         
27499
27500     },
27501     
27502     collapse : function(){
27503         Roo.form.ComboCheck.superclass.collapse.call(this);
27504         var sl = this.view.getSelectedIndexes();
27505         var st = this.store;
27506         var nv = [];
27507         var tv = [];
27508         var r;
27509         Roo.each(sl, function(i) {
27510             r = st.getAt(i);
27511             nv.push(r.get(this.valueField));
27512         },this);
27513         this.setValue(Roo.encode(nv));
27514         if (this.value != this.valueBefore) {
27515
27516             this.fireEvent('change', this, this.value, this.valueBefore);
27517             this.valueBefore = this.value;
27518         }
27519         
27520     },
27521     
27522     setValue : function(v){
27523         // Roo.log(v);
27524         this.value = v;
27525         
27526         var vals = this.getValueArray();
27527         var tv = [];
27528         Roo.each(vals, function(k) {
27529             var r = this.findRecord(this.valueField, k);
27530             if(r){
27531                 tv.push(r.data[this.displayField]);
27532             }else if(this.valueNotFoundText !== undefined){
27533                 tv.push( this.valueNotFoundText );
27534             }
27535         },this);
27536        // Roo.log(tv);
27537         
27538         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27539         this.hiddenField.value = v;
27540         this.value = v;
27541     }
27542     
27543 });/*
27544  * Based on:
27545  * Ext JS Library 1.1.1
27546  * Copyright(c) 2006-2007, Ext JS, LLC.
27547  *
27548  * Originally Released Under LGPL - original licence link has changed is not relivant.
27549  *
27550  * Fork - LGPL
27551  * <script type="text/javascript">
27552  */
27553  
27554 /**
27555  * @class Roo.form.Signature
27556  * @extends Roo.form.Field
27557  * Signature field.  
27558  * @constructor
27559  * 
27560  * @param {Object} config Configuration options
27561  */
27562
27563 Roo.form.Signature = function(config){
27564     Roo.form.Signature.superclass.constructor.call(this, config);
27565     
27566     this.addEvents({// not in used??
27567          /**
27568          * @event confirm
27569          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27570              * @param {Roo.form.Signature} combo This combo box
27571              */
27572         'confirm' : true,
27573         /**
27574          * @event reset
27575          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27576              * @param {Roo.form.ComboBox} combo This combo box
27577              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27578              */
27579         'reset' : true
27580     });
27581 };
27582
27583 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
27584     /**
27585      * @cfg {Object} labels Label to use when rendering a form.
27586      * defaults to 
27587      * labels : { 
27588      *      clear : "Clear",
27589      *      confirm : "Confirm"
27590      *  }
27591      */
27592     labels : { 
27593         clear : "Clear",
27594         confirm : "Confirm"
27595     },
27596     /**
27597      * @cfg {Number} width The signature panel width (defaults to 300)
27598      */
27599     width: 300,
27600     /**
27601      * @cfg {Number} height The signature panel height (defaults to 100)
27602      */
27603     height : 100,
27604     /**
27605      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27606      */
27607     allowBlank : false,
27608     
27609     //private
27610     // {Object} signPanel The signature SVG panel element (defaults to {})
27611     signPanel : {},
27612     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27613     isMouseDown : false,
27614     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27615     isConfirmed : false,
27616     // {String} signatureTmp SVG mapping string (defaults to empty string)
27617     signatureTmp : '',
27618     
27619     
27620     defaultAutoCreate : { // modified by initCompnoent..
27621         tag: "input",
27622         type:"hidden"
27623     },
27624
27625     // private
27626     onRender : function(ct, position){
27627         
27628         Roo.form.Signature.superclass.onRender.call(this, ct, position);
27629         
27630         this.wrap = this.el.wrap({
27631             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27632         });
27633         
27634         this.createToolbar(this);
27635         this.signPanel = this.wrap.createChild({
27636                 tag: 'div',
27637                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27638             }, this.el
27639         );
27640             
27641         this.svgID = Roo.id();
27642         this.svgEl = this.signPanel.createChild({
27643               xmlns : 'http://www.w3.org/2000/svg',
27644               tag : 'svg',
27645               id : this.svgID + "-svg",
27646               width: this.width,
27647               height: this.height,
27648               viewBox: '0 0 '+this.width+' '+this.height,
27649               cn : [
27650                 {
27651                     tag: "rect",
27652                     id: this.svgID + "-svg-r",
27653                     width: this.width,
27654                     height: this.height,
27655                     fill: "#ffa"
27656                 },
27657                 {
27658                     tag: "line",
27659                     id: this.svgID + "-svg-l",
27660                     x1: "0", // start
27661                     y1: (this.height*0.8), // start set the line in 80% of height
27662                     x2: this.width, // end
27663                     y2: (this.height*0.8), // end set the line in 80% of height
27664                     'stroke': "#666",
27665                     'stroke-width': "1",
27666                     'stroke-dasharray': "3",
27667                     'shape-rendering': "crispEdges",
27668                     'pointer-events': "none"
27669                 },
27670                 {
27671                     tag: "path",
27672                     id: this.svgID + "-svg-p",
27673                     'stroke': "navy",
27674                     'stroke-width': "3",
27675                     'fill': "none",
27676                     'pointer-events': 'none'
27677                 }
27678               ]
27679         });
27680         this.createSVG();
27681         this.svgBox = this.svgEl.dom.getScreenCTM();
27682     },
27683     createSVG : function(){ 
27684         var svg = this.signPanel;
27685         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27686         var t = this;
27687
27688         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27689         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27690         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27691         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27692         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27693         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27694         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27695         
27696     },
27697     isTouchEvent : function(e){
27698         return e.type.match(/^touch/);
27699     },
27700     getCoords : function (e) {
27701         var pt    = this.svgEl.dom.createSVGPoint();
27702         pt.x = e.clientX; 
27703         pt.y = e.clientY;
27704         if (this.isTouchEvent(e)) {
27705             pt.x =  e.targetTouches[0].clientX;
27706             pt.y = e.targetTouches[0].clientY;
27707         }
27708         var a = this.svgEl.dom.getScreenCTM();
27709         var b = a.inverse();
27710         var mx = pt.matrixTransform(b);
27711         return mx.x + ',' + mx.y;
27712     },
27713     //mouse event headler 
27714     down : function (e) {
27715         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27716         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27717         
27718         this.isMouseDown = true;
27719         
27720         e.preventDefault();
27721     },
27722     move : function (e) {
27723         if (this.isMouseDown) {
27724             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27725             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27726         }
27727         
27728         e.preventDefault();
27729     },
27730     up : function (e) {
27731         this.isMouseDown = false;
27732         var sp = this.signatureTmp.split(' ');
27733         
27734         if(sp.length > 1){
27735             if(!sp[sp.length-2].match(/^L/)){
27736                 sp.pop();
27737                 sp.pop();
27738                 sp.push("");
27739                 this.signatureTmp = sp.join(" ");
27740             }
27741         }
27742         if(this.getValue() != this.signatureTmp){
27743             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27744             this.isConfirmed = false;
27745         }
27746         e.preventDefault();
27747     },
27748     
27749     /**
27750      * Protected method that will not generally be called directly. It
27751      * is called when the editor creates its toolbar. Override this method if you need to
27752      * add custom toolbar buttons.
27753      * @param {HtmlEditor} editor
27754      */
27755     createToolbar : function(editor){
27756          function btn(id, toggle, handler){
27757             var xid = fid + '-'+ id ;
27758             return {
27759                 id : xid,
27760                 cmd : id,
27761                 cls : 'x-btn-icon x-edit-'+id,
27762                 enableToggle:toggle !== false,
27763                 scope: editor, // was editor...
27764                 handler:handler||editor.relayBtnCmd,
27765                 clickEvent:'mousedown',
27766                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27767                 tabIndex:-1
27768             };
27769         }
27770         
27771         
27772         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27773         this.tb = tb;
27774         this.tb.add(
27775            {
27776                 cls : ' x-signature-btn x-signature-'+id,
27777                 scope: editor, // was editor...
27778                 handler: this.reset,
27779                 clickEvent:'mousedown',
27780                 text: this.labels.clear
27781             },
27782             {
27783                  xtype : 'Fill',
27784                  xns: Roo.Toolbar
27785             }, 
27786             {
27787                 cls : '  x-signature-btn x-signature-'+id,
27788                 scope: editor, // was editor...
27789                 handler: this.confirmHandler,
27790                 clickEvent:'mousedown',
27791                 text: this.labels.confirm
27792             }
27793         );
27794     
27795     },
27796     //public
27797     /**
27798      * when user is clicked confirm then show this image.....
27799      * 
27800      * @return {String} Image Data URI
27801      */
27802     getImageDataURI : function(){
27803         var svg = this.svgEl.dom.parentNode.innerHTML;
27804         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
27805         return src; 
27806     },
27807     /**
27808      * 
27809      * @return {Boolean} this.isConfirmed
27810      */
27811     getConfirmed : function(){
27812         return this.isConfirmed;
27813     },
27814     /**
27815      * 
27816      * @return {Number} this.width
27817      */
27818     getWidth : function(){
27819         return this.width;
27820     },
27821     /**
27822      * 
27823      * @return {Number} this.height
27824      */
27825     getHeight : function(){
27826         return this.height;
27827     },
27828     // private
27829     getSignature : function(){
27830         return this.signatureTmp;
27831     },
27832     // private
27833     reset : function(){
27834         this.signatureTmp = '';
27835         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27836         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
27837         this.isConfirmed = false;
27838         Roo.form.Signature.superclass.reset.call(this);
27839     },
27840     setSignature : function(s){
27841         this.signatureTmp = s;
27842         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27843         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
27844         this.setValue(s);
27845         this.isConfirmed = false;
27846         Roo.form.Signature.superclass.reset.call(this);
27847     }, 
27848     test : function(){
27849 //        Roo.log(this.signPanel.dom.contentWindow.up())
27850     },
27851     //private
27852     setConfirmed : function(){
27853         
27854         
27855         
27856 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
27857     },
27858     // private
27859     confirmHandler : function(){
27860         if(!this.getSignature()){
27861             return;
27862         }
27863         
27864         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
27865         this.setValue(this.getSignature());
27866         this.isConfirmed = true;
27867         
27868         this.fireEvent('confirm', this);
27869     },
27870     // private
27871     // Subclasses should provide the validation implementation by overriding this
27872     validateValue : function(value){
27873         if(this.allowBlank){
27874             return true;
27875         }
27876         
27877         if(this.isConfirmed){
27878             return true;
27879         }
27880         return false;
27881     }
27882 });/*
27883  * Based on:
27884  * Ext JS Library 1.1.1
27885  * Copyright(c) 2006-2007, Ext JS, LLC.
27886  *
27887  * Originally Released Under LGPL - original licence link has changed is not relivant.
27888  *
27889  * Fork - LGPL
27890  * <script type="text/javascript">
27891  */
27892  
27893
27894 /**
27895  * @class Roo.form.ComboBox
27896  * @extends Roo.form.TriggerField
27897  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
27898  * @constructor
27899  * Create a new ComboBox.
27900  * @param {Object} config Configuration options
27901  */
27902 Roo.form.Select = function(config){
27903     Roo.form.Select.superclass.constructor.call(this, config);
27904      
27905 };
27906
27907 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
27908     /**
27909      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
27910      */
27911     /**
27912      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
27913      * rendering into an Roo.Editor, defaults to false)
27914      */
27915     /**
27916      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
27917      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
27918      */
27919     /**
27920      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
27921      */
27922     /**
27923      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
27924      * the dropdown list (defaults to undefined, with no header element)
27925      */
27926
27927      /**
27928      * @cfg {String/Roo.Template} tpl The template to use to render the output
27929      */
27930      
27931     // private
27932     defaultAutoCreate : {tag: "select"  },
27933     /**
27934      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
27935      */
27936     listWidth: undefined,
27937     /**
27938      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
27939      * mode = 'remote' or 'text' if mode = 'local')
27940      */
27941     displayField: undefined,
27942     /**
27943      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
27944      * mode = 'remote' or 'value' if mode = 'local'). 
27945      * Note: use of a valueField requires the user make a selection
27946      * in order for a value to be mapped.
27947      */
27948     valueField: undefined,
27949     
27950     
27951     /**
27952      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
27953      * field's data value (defaults to the underlying DOM element's name)
27954      */
27955     hiddenName: undefined,
27956     /**
27957      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
27958      */
27959     listClass: '',
27960     /**
27961      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
27962      */
27963     selectedClass: 'x-combo-selected',
27964     /**
27965      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
27966      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
27967      * which displays a downward arrow icon).
27968      */
27969     triggerClass : 'x-form-arrow-trigger',
27970     /**
27971      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
27972      */
27973     shadow:'sides',
27974     /**
27975      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
27976      * anchor positions (defaults to 'tl-bl')
27977      */
27978     listAlign: 'tl-bl?',
27979     /**
27980      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
27981      */
27982     maxHeight: 300,
27983     /**
27984      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
27985      * query specified by the allQuery config option (defaults to 'query')
27986      */
27987     triggerAction: 'query',
27988     /**
27989      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
27990      * (defaults to 4, does not apply if editable = false)
27991      */
27992     minChars : 4,
27993     /**
27994      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
27995      * delay (typeAheadDelay) if it matches a known value (defaults to false)
27996      */
27997     typeAhead: false,
27998     /**
27999      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
28000      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
28001      */
28002     queryDelay: 500,
28003     /**
28004      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
28005      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
28006      */
28007     pageSize: 0,
28008     /**
28009      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
28010      * when editable = true (defaults to false)
28011      */
28012     selectOnFocus:false,
28013     /**
28014      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
28015      */
28016     queryParam: 'query',
28017     /**
28018      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
28019      * when mode = 'remote' (defaults to 'Loading...')
28020      */
28021     loadingText: 'Loading...',
28022     /**
28023      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
28024      */
28025     resizable: false,
28026     /**
28027      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
28028      */
28029     handleHeight : 8,
28030     /**
28031      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
28032      * traditional select (defaults to true)
28033      */
28034     editable: true,
28035     /**
28036      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
28037      */
28038     allQuery: '',
28039     /**
28040      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
28041      */
28042     mode: 'remote',
28043     /**
28044      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
28045      * listWidth has a higher value)
28046      */
28047     minListWidth : 70,
28048     /**
28049      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
28050      * allow the user to set arbitrary text into the field (defaults to false)
28051      */
28052     forceSelection:false,
28053     /**
28054      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
28055      * if typeAhead = true (defaults to 250)
28056      */
28057     typeAheadDelay : 250,
28058     /**
28059      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
28060      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
28061      */
28062     valueNotFoundText : undefined,
28063     
28064     /**
28065      * @cfg {String} defaultValue The value displayed after loading the store.
28066      */
28067     defaultValue: '',
28068     
28069     /**
28070      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
28071      */
28072     blockFocus : false,
28073     
28074     /**
28075      * @cfg {Boolean} disableClear Disable showing of clear button.
28076      */
28077     disableClear : false,
28078     /**
28079      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
28080      */
28081     alwaysQuery : false,
28082     
28083     //private
28084     addicon : false,
28085     editicon: false,
28086     
28087     // element that contains real text value.. (when hidden is used..)
28088      
28089     // private
28090     onRender : function(ct, position){
28091         Roo.form.Field.prototype.onRender.call(this, ct, position);
28092         
28093         if(this.store){
28094             this.store.on('beforeload', this.onBeforeLoad, this);
28095             this.store.on('load', this.onLoad, this);
28096             this.store.on('loadexception', this.onLoadException, this);
28097             this.store.load({});
28098         }
28099         
28100         
28101         
28102     },
28103
28104     // private
28105     initEvents : function(){
28106         //Roo.form.ComboBox.superclass.initEvents.call(this);
28107  
28108     },
28109
28110     onDestroy : function(){
28111        
28112         if(this.store){
28113             this.store.un('beforeload', this.onBeforeLoad, this);
28114             this.store.un('load', this.onLoad, this);
28115             this.store.un('loadexception', this.onLoadException, this);
28116         }
28117         //Roo.form.ComboBox.superclass.onDestroy.call(this);
28118     },
28119
28120     // private
28121     fireKey : function(e){
28122         if(e.isNavKeyPress() && !this.list.isVisible()){
28123             this.fireEvent("specialkey", this, e);
28124         }
28125     },
28126
28127     // private
28128     onResize: function(w, h){
28129         
28130         return; 
28131     
28132         
28133     },
28134
28135     /**
28136      * Allow or prevent the user from directly editing the field text.  If false is passed,
28137      * the user will only be able to select from the items defined in the dropdown list.  This method
28138      * is the runtime equivalent of setting the 'editable' config option at config time.
28139      * @param {Boolean} value True to allow the user to directly edit the field text
28140      */
28141     setEditable : function(value){
28142          
28143     },
28144
28145     // private
28146     onBeforeLoad : function(){
28147         
28148         Roo.log("Select before load");
28149         return;
28150     
28151         this.innerList.update(this.loadingText ?
28152                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
28153         //this.restrictHeight();
28154         this.selectedIndex = -1;
28155     },
28156
28157     // private
28158     onLoad : function(){
28159
28160     
28161         var dom = this.el.dom;
28162         dom.innerHTML = '';
28163          var od = dom.ownerDocument;
28164          
28165         if (this.emptyText) {
28166             var op = od.createElement('option');
28167             op.setAttribute('value', '');
28168             op.innerHTML = String.format('{0}', this.emptyText);
28169             dom.appendChild(op);
28170         }
28171         if(this.store.getCount() > 0){
28172            
28173             var vf = this.valueField;
28174             var df = this.displayField;
28175             this.store.data.each(function(r) {
28176                 // which colmsn to use... testing - cdoe / title..
28177                 var op = od.createElement('option');
28178                 op.setAttribute('value', r.data[vf]);
28179                 op.innerHTML = String.format('{0}', r.data[df]);
28180                 dom.appendChild(op);
28181             });
28182             if (typeof(this.defaultValue != 'undefined')) {
28183                 this.setValue(this.defaultValue);
28184             }
28185             
28186              
28187         }else{
28188             //this.onEmptyResults();
28189         }
28190         //this.el.focus();
28191     },
28192     // private
28193     onLoadException : function()
28194     {
28195         dom.innerHTML = '';
28196             
28197         Roo.log("Select on load exception");
28198         return;
28199     
28200         this.collapse();
28201         Roo.log(this.store.reader.jsonData);
28202         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
28203             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
28204         }
28205         
28206         
28207     },
28208     // private
28209     onTypeAhead : function(){
28210          
28211     },
28212
28213     // private
28214     onSelect : function(record, index){
28215         Roo.log('on select?');
28216         return;
28217         if(this.fireEvent('beforeselect', this, record, index) !== false){
28218             this.setFromData(index > -1 ? record.data : false);
28219             this.collapse();
28220             this.fireEvent('select', this, record, index);
28221         }
28222     },
28223
28224     /**
28225      * Returns the currently selected field value or empty string if no value is set.
28226      * @return {String} value The selected value
28227      */
28228     getValue : function(){
28229         var dom = this.el.dom;
28230         this.value = dom.options[dom.selectedIndex].value;
28231         return this.value;
28232         
28233     },
28234
28235     /**
28236      * Clears any text/value currently set in the field
28237      */
28238     clearValue : function(){
28239         this.value = '';
28240         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
28241         
28242     },
28243
28244     /**
28245      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
28246      * will be displayed in the field.  If the value does not match the data value of an existing item,
28247      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
28248      * Otherwise the field will be blank (although the value will still be set).
28249      * @param {String} value The value to match
28250      */
28251     setValue : function(v){
28252         var d = this.el.dom;
28253         for (var i =0; i < d.options.length;i++) {
28254             if (v == d.options[i].value) {
28255                 d.selectedIndex = i;
28256                 this.value = v;
28257                 return;
28258             }
28259         }
28260         this.clearValue();
28261     },
28262     /**
28263      * @property {Object} the last set data for the element
28264      */
28265     
28266     lastData : false,
28267     /**
28268      * Sets the value of the field based on a object which is related to the record format for the store.
28269      * @param {Object} value the value to set as. or false on reset?
28270      */
28271     setFromData : function(o){
28272         Roo.log('setfrom data?');
28273          
28274         
28275         
28276     },
28277     // private
28278     reset : function(){
28279         this.clearValue();
28280     },
28281     // private
28282     findRecord : function(prop, value){
28283         
28284         return false;
28285     
28286         var record;
28287         if(this.store.getCount() > 0){
28288             this.store.each(function(r){
28289                 if(r.data[prop] == value){
28290                     record = r;
28291                     return false;
28292                 }
28293                 return true;
28294             });
28295         }
28296         return record;
28297     },
28298     
28299     getName: function()
28300     {
28301         // returns hidden if it's set..
28302         if (!this.rendered) {return ''};
28303         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
28304         
28305     },
28306      
28307
28308     
28309
28310     // private
28311     onEmptyResults : function(){
28312         Roo.log('empty results');
28313         //this.collapse();
28314     },
28315
28316     /**
28317      * Returns true if the dropdown list is expanded, else false.
28318      */
28319     isExpanded : function(){
28320         return false;
28321     },
28322
28323     /**
28324      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
28325      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28326      * @param {String} value The data value of the item to select
28327      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28328      * selected item if it is not currently in view (defaults to true)
28329      * @return {Boolean} True if the value matched an item in the list, else false
28330      */
28331     selectByValue : function(v, scrollIntoView){
28332         Roo.log('select By Value');
28333         return false;
28334     
28335         if(v !== undefined && v !== null){
28336             var r = this.findRecord(this.valueField || this.displayField, v);
28337             if(r){
28338                 this.select(this.store.indexOf(r), scrollIntoView);
28339                 return true;
28340             }
28341         }
28342         return false;
28343     },
28344
28345     /**
28346      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
28347      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28348      * @param {Number} index The zero-based index of the list item to select
28349      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28350      * selected item if it is not currently in view (defaults to true)
28351      */
28352     select : function(index, scrollIntoView){
28353         Roo.log('select ');
28354         return  ;
28355         
28356         this.selectedIndex = index;
28357         this.view.select(index);
28358         if(scrollIntoView !== false){
28359             var el = this.view.getNode(index);
28360             if(el){
28361                 this.innerList.scrollChildIntoView(el, false);
28362             }
28363         }
28364     },
28365
28366       
28367
28368     // private
28369     validateBlur : function(){
28370         
28371         return;
28372         
28373     },
28374
28375     // private
28376     initQuery : function(){
28377         this.doQuery(this.getRawValue());
28378     },
28379
28380     // private
28381     doForce : function(){
28382         if(this.el.dom.value.length > 0){
28383             this.el.dom.value =
28384                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
28385              
28386         }
28387     },
28388
28389     /**
28390      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
28391      * query allowing the query action to be canceled if needed.
28392      * @param {String} query The SQL query to execute
28393      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
28394      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
28395      * saved in the current store (defaults to false)
28396      */
28397     doQuery : function(q, forceAll){
28398         
28399         Roo.log('doQuery?');
28400         if(q === undefined || q === null){
28401             q = '';
28402         }
28403         var qe = {
28404             query: q,
28405             forceAll: forceAll,
28406             combo: this,
28407             cancel:false
28408         };
28409         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
28410             return false;
28411         }
28412         q = qe.query;
28413         forceAll = qe.forceAll;
28414         if(forceAll === true || (q.length >= this.minChars)){
28415             if(this.lastQuery != q || this.alwaysQuery){
28416                 this.lastQuery = q;
28417                 if(this.mode == 'local'){
28418                     this.selectedIndex = -1;
28419                     if(forceAll){
28420                         this.store.clearFilter();
28421                     }else{
28422                         this.store.filter(this.displayField, q);
28423                     }
28424                     this.onLoad();
28425                 }else{
28426                     this.store.baseParams[this.queryParam] = q;
28427                     this.store.load({
28428                         params: this.getParams(q)
28429                     });
28430                     this.expand();
28431                 }
28432             }else{
28433                 this.selectedIndex = -1;
28434                 this.onLoad();   
28435             }
28436         }
28437     },
28438
28439     // private
28440     getParams : function(q){
28441         var p = {};
28442         //p[this.queryParam] = q;
28443         if(this.pageSize){
28444             p.start = 0;
28445             p.limit = this.pageSize;
28446         }
28447         return p;
28448     },
28449
28450     /**
28451      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
28452      */
28453     collapse : function(){
28454         
28455     },
28456
28457     // private
28458     collapseIf : function(e){
28459         
28460     },
28461
28462     /**
28463      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28464      */
28465     expand : function(){
28466         
28467     } ,
28468
28469     // private
28470      
28471
28472     /** 
28473     * @cfg {Boolean} grow 
28474     * @hide 
28475     */
28476     /** 
28477     * @cfg {Number} growMin 
28478     * @hide 
28479     */
28480     /** 
28481     * @cfg {Number} growMax 
28482     * @hide 
28483     */
28484     /**
28485      * @hide
28486      * @method autoSize
28487      */
28488     
28489     setWidth : function()
28490     {
28491         
28492     },
28493     getResizeEl : function(){
28494         return this.el;
28495     }
28496 });//<script type="text/javasscript">
28497  
28498
28499 /**
28500  * @class Roo.DDView
28501  * A DnD enabled version of Roo.View.
28502  * @param {Element/String} container The Element in which to create the View.
28503  * @param {String} tpl The template string used to create the markup for each element of the View
28504  * @param {Object} config The configuration properties. These include all the config options of
28505  * {@link Roo.View} plus some specific to this class.<br>
28506  * <p>
28507  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28508  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28509  * <p>
28510  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28511 .x-view-drag-insert-above {
28512         border-top:1px dotted #3366cc;
28513 }
28514 .x-view-drag-insert-below {
28515         border-bottom:1px dotted #3366cc;
28516 }
28517 </code></pre>
28518  * 
28519  */
28520  
28521 Roo.DDView = function(container, tpl, config) {
28522     Roo.DDView.superclass.constructor.apply(this, arguments);
28523     this.getEl().setStyle("outline", "0px none");
28524     this.getEl().unselectable();
28525     if (this.dragGroup) {
28526         this.setDraggable(this.dragGroup.split(","));
28527     }
28528     if (this.dropGroup) {
28529         this.setDroppable(this.dropGroup.split(","));
28530     }
28531     if (this.deletable) {
28532         this.setDeletable();
28533     }
28534     this.isDirtyFlag = false;
28535         this.addEvents({
28536                 "drop" : true
28537         });
28538 };
28539
28540 Roo.extend(Roo.DDView, Roo.View, {
28541 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28542 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28543 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28544 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28545
28546         isFormField: true,
28547
28548         reset: Roo.emptyFn,
28549         
28550         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28551
28552         validate: function() {
28553                 return true;
28554         },
28555         
28556         destroy: function() {
28557                 this.purgeListeners();
28558                 this.getEl.removeAllListeners();
28559                 this.getEl().remove();
28560                 if (this.dragZone) {
28561                         if (this.dragZone.destroy) {
28562                                 this.dragZone.destroy();
28563                         }
28564                 }
28565                 if (this.dropZone) {
28566                         if (this.dropZone.destroy) {
28567                                 this.dropZone.destroy();
28568                         }
28569                 }
28570         },
28571
28572 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28573         getName: function() {
28574                 return this.name;
28575         },
28576
28577 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28578         setValue: function(v) {
28579                 if (!this.store) {
28580                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28581                 }
28582                 var data = {};
28583                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28584                 this.store.proxy = new Roo.data.MemoryProxy(data);
28585                 this.store.load();
28586         },
28587
28588 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28589         getValue: function() {
28590                 var result = '(';
28591                 this.store.each(function(rec) {
28592                         result += rec.id + ',';
28593                 });
28594                 return result.substr(0, result.length - 1) + ')';
28595         },
28596         
28597         getIds: function() {
28598                 var i = 0, result = new Array(this.store.getCount());
28599                 this.store.each(function(rec) {
28600                         result[i++] = rec.id;
28601                 });
28602                 return result;
28603         },
28604         
28605         isDirty: function() {
28606                 return this.isDirtyFlag;
28607         },
28608
28609 /**
28610  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28611  *      whole Element becomes the target, and this causes the drop gesture to append.
28612  */
28613     getTargetFromEvent : function(e) {
28614                 var target = e.getTarget();
28615                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28616                 target = target.parentNode;
28617                 }
28618                 if (!target) {
28619                         target = this.el.dom.lastChild || this.el.dom;
28620                 }
28621                 return target;
28622     },
28623
28624 /**
28625  *      Create the drag data which consists of an object which has the property "ddel" as
28626  *      the drag proxy element. 
28627  */
28628     getDragData : function(e) {
28629         var target = this.findItemFromChild(e.getTarget());
28630                 if(target) {
28631                         this.handleSelection(e);
28632                         var selNodes = this.getSelectedNodes();
28633             var dragData = {
28634                 source: this,
28635                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28636                 nodes: selNodes,
28637                 records: []
28638                         };
28639                         var selectedIndices = this.getSelectedIndexes();
28640                         for (var i = 0; i < selectedIndices.length; i++) {
28641                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28642                         }
28643                         if (selNodes.length == 1) {
28644                                 dragData.ddel = target.cloneNode(true); // the div element
28645                         } else {
28646                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28647                                 div.className = 'multi-proxy';
28648                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28649                                         div.appendChild(selNodes[i].cloneNode(true));
28650                                 }
28651                                 dragData.ddel = div;
28652                         }
28653             //console.log(dragData)
28654             //console.log(dragData.ddel.innerHTML)
28655                         return dragData;
28656                 }
28657         //console.log('nodragData')
28658                 return false;
28659     },
28660     
28661 /**     Specify to which ddGroup items in this DDView may be dragged. */
28662     setDraggable: function(ddGroup) {
28663         if (ddGroup instanceof Array) {
28664                 Roo.each(ddGroup, this.setDraggable, this);
28665                 return;
28666         }
28667         if (this.dragZone) {
28668                 this.dragZone.addToGroup(ddGroup);
28669         } else {
28670                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28671                                 containerScroll: true,
28672                                 ddGroup: ddGroup 
28673
28674                         });
28675 //                      Draggability implies selection. DragZone's mousedown selects the element.
28676                         if (!this.multiSelect) { this.singleSelect = true; }
28677
28678 //                      Wire the DragZone's handlers up to methods in *this*
28679                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28680                 }
28681     },
28682
28683 /**     Specify from which ddGroup this DDView accepts drops. */
28684     setDroppable: function(ddGroup) {
28685         if (ddGroup instanceof Array) {
28686                 Roo.each(ddGroup, this.setDroppable, this);
28687                 return;
28688         }
28689         if (this.dropZone) {
28690                 this.dropZone.addToGroup(ddGroup);
28691         } else {
28692                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28693                                 containerScroll: true,
28694                                 ddGroup: ddGroup
28695                         });
28696
28697 //                      Wire the DropZone's handlers up to methods in *this*
28698                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28699                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28700                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28701                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28702                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28703                 }
28704     },
28705
28706 /**     Decide whether to drop above or below a View node. */
28707     getDropPoint : function(e, n, dd){
28708         if (n == this.el.dom) { return "above"; }
28709                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28710                 var c = t + (b - t) / 2;
28711                 var y = Roo.lib.Event.getPageY(e);
28712                 if(y <= c) {
28713                         return "above";
28714                 }else{
28715                         return "below";
28716                 }
28717     },
28718
28719     onNodeEnter : function(n, dd, e, data){
28720                 return false;
28721     },
28722     
28723     onNodeOver : function(n, dd, e, data){
28724                 var pt = this.getDropPoint(e, n, dd);
28725                 // set the insert point style on the target node
28726                 var dragElClass = this.dropNotAllowed;
28727                 if (pt) {
28728                         var targetElClass;
28729                         if (pt == "above"){
28730                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28731                                 targetElClass = "x-view-drag-insert-above";
28732                         } else {
28733                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28734                                 targetElClass = "x-view-drag-insert-below";
28735                         }
28736                         if (this.lastInsertClass != targetElClass){
28737                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28738                                 this.lastInsertClass = targetElClass;
28739                         }
28740                 }
28741                 return dragElClass;
28742         },
28743
28744     onNodeOut : function(n, dd, e, data){
28745                 this.removeDropIndicators(n);
28746     },
28747
28748     onNodeDrop : function(n, dd, e, data){
28749         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28750                 return false;
28751         }
28752         var pt = this.getDropPoint(e, n, dd);
28753                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28754                 if (pt == "below") { insertAt++; }
28755                 for (var i = 0; i < data.records.length; i++) {
28756                         var r = data.records[i];
28757                         var dup = this.store.getById(r.id);
28758                         if (dup && (dd != this.dragZone)) {
28759                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28760                         } else {
28761                                 if (data.copy) {
28762                                         this.store.insert(insertAt++, r.copy());
28763                                 } else {
28764                                         data.source.isDirtyFlag = true;
28765                                         r.store.remove(r);
28766                                         this.store.insert(insertAt++, r);
28767                                 }
28768                                 this.isDirtyFlag = true;
28769                         }
28770                 }
28771                 this.dragZone.cachedTarget = null;
28772                 return true;
28773     },
28774
28775     removeDropIndicators : function(n){
28776                 if(n){
28777                         Roo.fly(n).removeClass([
28778                                 "x-view-drag-insert-above",
28779                                 "x-view-drag-insert-below"]);
28780                         this.lastInsertClass = "_noclass";
28781                 }
28782     },
28783
28784 /**
28785  *      Utility method. Add a delete option to the DDView's context menu.
28786  *      @param {String} imageUrl The URL of the "delete" icon image.
28787  */
28788         setDeletable: function(imageUrl) {
28789                 if (!this.singleSelect && !this.multiSelect) {
28790                         this.singleSelect = true;
28791                 }
28792                 var c = this.getContextMenu();
28793                 this.contextMenu.on("itemclick", function(item) {
28794                         switch (item.id) {
28795                                 case "delete":
28796                                         this.remove(this.getSelectedIndexes());
28797                                         break;
28798                         }
28799                 }, this);
28800                 this.contextMenu.add({
28801                         icon: imageUrl,
28802                         id: "delete",
28803                         text: 'Delete'
28804                 });
28805         },
28806         
28807 /**     Return the context menu for this DDView. */
28808         getContextMenu: function() {
28809                 if (!this.contextMenu) {
28810 //                      Create the View's context menu
28811                         this.contextMenu = new Roo.menu.Menu({
28812                                 id: this.id + "-contextmenu"
28813                         });
28814                         this.el.on("contextmenu", this.showContextMenu, this);
28815                 }
28816                 return this.contextMenu;
28817         },
28818         
28819         disableContextMenu: function() {
28820                 if (this.contextMenu) {
28821                         this.el.un("contextmenu", this.showContextMenu, this);
28822                 }
28823         },
28824
28825         showContextMenu: function(e, item) {
28826         item = this.findItemFromChild(e.getTarget());
28827                 if (item) {
28828                         e.stopEvent();
28829                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28830                         this.contextMenu.showAt(e.getXY());
28831             }
28832     },
28833
28834 /**
28835  *      Remove {@link Roo.data.Record}s at the specified indices.
28836  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28837  */
28838     remove: function(selectedIndices) {
28839                 selectedIndices = [].concat(selectedIndices);
28840                 for (var i = 0; i < selectedIndices.length; i++) {
28841                         var rec = this.store.getAt(selectedIndices[i]);
28842                         this.store.remove(rec);
28843                 }
28844     },
28845
28846 /**
28847  *      Double click fires the event, but also, if this is draggable, and there is only one other
28848  *      related DropZone, it transfers the selected node.
28849  */
28850     onDblClick : function(e){
28851         var item = this.findItemFromChild(e.getTarget());
28852         if(item){
28853             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28854                 return false;
28855             }
28856             if (this.dragGroup) {
28857                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28858                     while (targets.indexOf(this.dropZone) > -1) {
28859                             targets.remove(this.dropZone);
28860                                 }
28861                     if (targets.length == 1) {
28862                                         this.dragZone.cachedTarget = null;
28863                         var el = Roo.get(targets[0].getEl());
28864                         var box = el.getBox(true);
28865                         targets[0].onNodeDrop(el.dom, {
28866                                 target: el.dom,
28867                                 xy: [box.x, box.y + box.height - 1]
28868                         }, null, this.getDragData(e));
28869                     }
28870                 }
28871         }
28872     },
28873     
28874     handleSelection: function(e) {
28875                 this.dragZone.cachedTarget = null;
28876         var item = this.findItemFromChild(e.getTarget());
28877         if (!item) {
28878                 this.clearSelections(true);
28879                 return;
28880         }
28881                 if (item && (this.multiSelect || this.singleSelect)){
28882                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28883                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28884                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28885                                 this.unselect(item);
28886                         } else {
28887                                 this.select(item, this.multiSelect && e.ctrlKey);
28888                                 this.lastSelection = item;
28889                         }
28890                 }
28891     },
28892
28893     onItemClick : function(item, index, e){
28894                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28895                         return false;
28896                 }
28897                 return true;
28898     },
28899
28900     unselect : function(nodeInfo, suppressEvent){
28901                 var node = this.getNode(nodeInfo);
28902                 if(node && this.isSelected(node)){
28903                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28904                                 Roo.fly(node).removeClass(this.selectedClass);
28905                                 this.selections.remove(node);
28906                                 if(!suppressEvent){
28907                                         this.fireEvent("selectionchange", this, this.selections);
28908                                 }
28909                         }
28910                 }
28911     }
28912 });
28913 /*
28914  * Based on:
28915  * Ext JS Library 1.1.1
28916  * Copyright(c) 2006-2007, Ext JS, LLC.
28917  *
28918  * Originally Released Under LGPL - original licence link has changed is not relivant.
28919  *
28920  * Fork - LGPL
28921  * <script type="text/javascript">
28922  */
28923  
28924 /**
28925  * @class Roo.LayoutManager
28926  * @extends Roo.util.Observable
28927  * Base class for layout managers.
28928  */
28929 Roo.LayoutManager = function(container, config){
28930     Roo.LayoutManager.superclass.constructor.call(this);
28931     this.el = Roo.get(container);
28932     // ie scrollbar fix
28933     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28934         document.body.scroll = "no";
28935     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28936         this.el.position('relative');
28937     }
28938     this.id = this.el.id;
28939     this.el.addClass("x-layout-container");
28940     /** false to disable window resize monitoring @type Boolean */
28941     this.monitorWindowResize = true;
28942     this.regions = {};
28943     this.addEvents({
28944         /**
28945          * @event layout
28946          * Fires when a layout is performed. 
28947          * @param {Roo.LayoutManager} this
28948          */
28949         "layout" : true,
28950         /**
28951          * @event regionresized
28952          * Fires when the user resizes a region. 
28953          * @param {Roo.LayoutRegion} region The resized region
28954          * @param {Number} newSize The new size (width for east/west, height for north/south)
28955          */
28956         "regionresized" : true,
28957         /**
28958          * @event regioncollapsed
28959          * Fires when a region is collapsed. 
28960          * @param {Roo.LayoutRegion} region The collapsed region
28961          */
28962         "regioncollapsed" : true,
28963         /**
28964          * @event regionexpanded
28965          * Fires when a region is expanded.  
28966          * @param {Roo.LayoutRegion} region The expanded region
28967          */
28968         "regionexpanded" : true
28969     });
28970     this.updating = false;
28971     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28972 };
28973
28974 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28975     /**
28976      * Returns true if this layout is currently being updated
28977      * @return {Boolean}
28978      */
28979     isUpdating : function(){
28980         return this.updating; 
28981     },
28982     
28983     /**
28984      * Suspend the LayoutManager from doing auto-layouts while
28985      * making multiple add or remove calls
28986      */
28987     beginUpdate : function(){
28988         this.updating = true;    
28989     },
28990     
28991     /**
28992      * Restore auto-layouts and optionally disable the manager from performing a layout
28993      * @param {Boolean} noLayout true to disable a layout update 
28994      */
28995     endUpdate : function(noLayout){
28996         this.updating = false;
28997         if(!noLayout){
28998             this.layout();
28999         }    
29000     },
29001     
29002     layout: function(){
29003         
29004     },
29005     
29006     onRegionResized : function(region, newSize){
29007         this.fireEvent("regionresized", region, newSize);
29008         this.layout();
29009     },
29010     
29011     onRegionCollapsed : function(region){
29012         this.fireEvent("regioncollapsed", region);
29013     },
29014     
29015     onRegionExpanded : function(region){
29016         this.fireEvent("regionexpanded", region);
29017     },
29018         
29019     /**
29020      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
29021      * performs box-model adjustments.
29022      * @return {Object} The size as an object {width: (the width), height: (the height)}
29023      */
29024     getViewSize : function(){
29025         var size;
29026         if(this.el.dom != document.body){
29027             size = this.el.getSize();
29028         }else{
29029             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
29030         }
29031         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
29032         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29033         return size;
29034     },
29035     
29036     /**
29037      * Returns the Element this layout is bound to.
29038      * @return {Roo.Element}
29039      */
29040     getEl : function(){
29041         return this.el;
29042     },
29043     
29044     /**
29045      * Returns the specified region.
29046      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
29047      * @return {Roo.LayoutRegion}
29048      */
29049     getRegion : function(target){
29050         return this.regions[target.toLowerCase()];
29051     },
29052     
29053     onWindowResize : function(){
29054         if(this.monitorWindowResize){
29055             this.layout();
29056         }
29057     }
29058 });/*
29059  * Based on:
29060  * Ext JS Library 1.1.1
29061  * Copyright(c) 2006-2007, Ext JS, LLC.
29062  *
29063  * Originally Released Under LGPL - original licence link has changed is not relivant.
29064  *
29065  * Fork - LGPL
29066  * <script type="text/javascript">
29067  */
29068 /**
29069  * @class Roo.BorderLayout
29070  * @extends Roo.LayoutManager
29071  * @children Roo.ContentPanel
29072  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
29073  * please see: <br><br>
29074  * <a href="http://www.jackslocum.com/yui/2006/10/19/cross-browser-web-20-layouts-with-yahoo-ui/">Cross Browser Layouts - Part 1</a><br>
29075  * <a href="http://www.jackslocum.com/yui/2006/10/28/cross-browser-web-20-layouts-part-2-ajax-feed-viewer-20/">Cross Browser Layouts - Part 2</a><br><br>
29076  * Example:
29077  <pre><code>
29078  var layout = new Roo.BorderLayout(document.body, {
29079     north: {
29080         initialSize: 25,
29081         titlebar: false
29082     },
29083     west: {
29084         split:true,
29085         initialSize: 200,
29086         minSize: 175,
29087         maxSize: 400,
29088         titlebar: true,
29089         collapsible: true
29090     },
29091     east: {
29092         split:true,
29093         initialSize: 202,
29094         minSize: 175,
29095         maxSize: 400,
29096         titlebar: true,
29097         collapsible: true
29098     },
29099     south: {
29100         split:true,
29101         initialSize: 100,
29102         minSize: 100,
29103         maxSize: 200,
29104         titlebar: true,
29105         collapsible: true
29106     },
29107     center: {
29108         titlebar: true,
29109         autoScroll:true,
29110         resizeTabs: true,
29111         minTabWidth: 50,
29112         preferredTabWidth: 150
29113     }
29114 });
29115
29116 // shorthand
29117 var CP = Roo.ContentPanel;
29118
29119 layout.beginUpdate();
29120 layout.add("north", new CP("north", "North"));
29121 layout.add("south", new CP("south", {title: "South", closable: true}));
29122 layout.add("west", new CP("west", {title: "West"}));
29123 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29124 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29125 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29126 layout.getRegion("center").showPanel("center1");
29127 layout.endUpdate();
29128 </code></pre>
29129
29130 <b>The container the layout is rendered into can be either the body element or any other element.
29131 If it is not the body element, the container needs to either be an absolute positioned element,
29132 or you will need to add "position:relative" to the css of the container.  You will also need to specify
29133 the container size if it is not the body element.</b>
29134
29135 * @constructor
29136 * Create a new BorderLayout
29137 * @param {String/HTMLElement/Element} container The container this layout is bound to
29138 * @param {Object} config Configuration options
29139  */
29140 Roo.BorderLayout = function(container, config){
29141     config = config || {};
29142     Roo.BorderLayout.superclass.constructor.call(this, container, config);
29143     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29144     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29145         var target = this.factory.validRegions[i];
29146         if(config[target]){
29147             this.addRegion(target, config[target]);
29148         }
29149     }
29150 };
29151
29152 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29153         
29154         /**
29155          * @cfg {Roo.LayoutRegion} east
29156          */
29157         /**
29158          * @cfg {Roo.LayoutRegion} west
29159          */
29160         /**
29161          * @cfg {Roo.LayoutRegion} north
29162          */
29163         /**
29164          * @cfg {Roo.LayoutRegion} south
29165          */
29166         /**
29167          * @cfg {Roo.LayoutRegion} center
29168          */
29169     /**
29170      * Creates and adds a new region if it doesn't already exist.
29171      * @param {String} target The target region key (north, south, east, west or center).
29172      * @param {Object} config The regions config object
29173      * @return {BorderLayoutRegion} The new region
29174      */
29175     addRegion : function(target, config){
29176         if(!this.regions[target]){
29177             var r = this.factory.create(target, this, config);
29178             this.bindRegion(target, r);
29179         }
29180         return this.regions[target];
29181     },
29182
29183     // private (kinda)
29184     bindRegion : function(name, r){
29185         this.regions[name] = r;
29186         r.on("visibilitychange", this.layout, this);
29187         r.on("paneladded", this.layout, this);
29188         r.on("panelremoved", this.layout, this);
29189         r.on("invalidated", this.layout, this);
29190         r.on("resized", this.onRegionResized, this);
29191         r.on("collapsed", this.onRegionCollapsed, this);
29192         r.on("expanded", this.onRegionExpanded, this);
29193     },
29194
29195     /**
29196      * Performs a layout update.
29197      */
29198     layout : function(){
29199         if(this.updating) {
29200             return;
29201         }
29202         var size = this.getViewSize();
29203         var w = size.width;
29204         var h = size.height;
29205         var centerW = w;
29206         var centerH = h;
29207         var centerY = 0;
29208         var centerX = 0;
29209         //var x = 0, y = 0;
29210
29211         var rs = this.regions;
29212         var north = rs["north"];
29213         var south = rs["south"]; 
29214         var west = rs["west"];
29215         var east = rs["east"];
29216         var center = rs["center"];
29217         //if(this.hideOnLayout){ // not supported anymore
29218             //c.el.setStyle("display", "none");
29219         //}
29220         if(north && north.isVisible()){
29221             var b = north.getBox();
29222             var m = north.getMargins();
29223             b.width = w - (m.left+m.right);
29224             b.x = m.left;
29225             b.y = m.top;
29226             centerY = b.height + b.y + m.bottom;
29227             centerH -= centerY;
29228             north.updateBox(this.safeBox(b));
29229         }
29230         if(south && south.isVisible()){
29231             var b = south.getBox();
29232             var m = south.getMargins();
29233             b.width = w - (m.left+m.right);
29234             b.x = m.left;
29235             var totalHeight = (b.height + m.top + m.bottom);
29236             b.y = h - totalHeight + m.top;
29237             centerH -= totalHeight;
29238             south.updateBox(this.safeBox(b));
29239         }
29240         if(west && west.isVisible()){
29241             var b = west.getBox();
29242             var m = west.getMargins();
29243             b.height = centerH - (m.top+m.bottom);
29244             b.x = m.left;
29245             b.y = centerY + m.top;
29246             var totalWidth = (b.width + m.left + m.right);
29247             centerX += totalWidth;
29248             centerW -= totalWidth;
29249             west.updateBox(this.safeBox(b));
29250         }
29251         if(east && east.isVisible()){
29252             var b = east.getBox();
29253             var m = east.getMargins();
29254             b.height = centerH - (m.top+m.bottom);
29255             var totalWidth = (b.width + m.left + m.right);
29256             b.x = w - totalWidth + m.left;
29257             b.y = centerY + m.top;
29258             centerW -= totalWidth;
29259             east.updateBox(this.safeBox(b));
29260         }
29261         if(center){
29262             var m = center.getMargins();
29263             var centerBox = {
29264                 x: centerX + m.left,
29265                 y: centerY + m.top,
29266                 width: centerW - (m.left+m.right),
29267                 height: centerH - (m.top+m.bottom)
29268             };
29269             //if(this.hideOnLayout){
29270                 //center.el.setStyle("display", "block");
29271             //}
29272             center.updateBox(this.safeBox(centerBox));
29273         }
29274         this.el.repaint();
29275         this.fireEvent("layout", this);
29276     },
29277
29278     // private
29279     safeBox : function(box){
29280         box.width = Math.max(0, box.width);
29281         box.height = Math.max(0, box.height);
29282         return box;
29283     },
29284
29285     /**
29286      * Adds a ContentPanel (or subclass) to this layout.
29287      * @param {String} target The target region key (north, south, east, west or center).
29288      * @param {Roo.ContentPanel} panel The panel to add
29289      * @return {Roo.ContentPanel} The added panel
29290      */
29291     add : function(target, panel){
29292          
29293         target = target.toLowerCase();
29294         return this.regions[target].add(panel);
29295     },
29296
29297     /**
29298      * Remove a ContentPanel (or subclass) to this layout.
29299      * @param {String} target The target region key (north, south, east, west or center).
29300      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29301      * @return {Roo.ContentPanel} The removed panel
29302      */
29303     remove : function(target, panel){
29304         target = target.toLowerCase();
29305         return this.regions[target].remove(panel);
29306     },
29307
29308     /**
29309      * Searches all regions for a panel with the specified id
29310      * @param {String} panelId
29311      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29312      */
29313     findPanel : function(panelId){
29314         var rs = this.regions;
29315         for(var target in rs){
29316             if(typeof rs[target] != "function"){
29317                 var p = rs[target].getPanel(panelId);
29318                 if(p){
29319                     return p;
29320                 }
29321             }
29322         }
29323         return null;
29324     },
29325
29326     /**
29327      * Searches all regions for a panel with the specified id and activates (shows) it.
29328      * @param {String/ContentPanel} panelId The panels id or the panel itself
29329      * @return {Roo.ContentPanel} The shown panel or null
29330      */
29331     showPanel : function(panelId) {
29332       var rs = this.regions;
29333       for(var target in rs){
29334          var r = rs[target];
29335          if(typeof r != "function"){
29336             if(r.hasPanel(panelId)){
29337                return r.showPanel(panelId);
29338             }
29339          }
29340       }
29341       return null;
29342    },
29343
29344    /**
29345      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29346      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29347      */
29348     restoreState : function(provider){
29349         if(!provider){
29350             provider = Roo.state.Manager;
29351         }
29352         var sm = new Roo.LayoutStateManager();
29353         sm.init(this, provider);
29354     },
29355
29356     /**
29357      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29358      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29359      * a valid ContentPanel config object.  Example:
29360      * <pre><code>
29361 // Create the main layout
29362 var layout = new Roo.BorderLayout('main-ct', {
29363     west: {
29364         split:true,
29365         minSize: 175,
29366         titlebar: true
29367     },
29368     center: {
29369         title:'Components'
29370     }
29371 }, 'main-ct');
29372
29373 // Create and add multiple ContentPanels at once via configs
29374 layout.batchAdd({
29375    west: {
29376        id: 'source-files',
29377        autoCreate:true,
29378        title:'Ext Source Files',
29379        autoScroll:true,
29380        fitToFrame:true
29381    },
29382    center : {
29383        el: cview,
29384        autoScroll:true,
29385        fitToFrame:true,
29386        toolbar: tb,
29387        resizeEl:'cbody'
29388    }
29389 });
29390 </code></pre>
29391      * @param {Object} regions An object containing ContentPanel configs by region name
29392      */
29393     batchAdd : function(regions){
29394         this.beginUpdate();
29395         for(var rname in regions){
29396             var lr = this.regions[rname];
29397             if(lr){
29398                 this.addTypedPanels(lr, regions[rname]);
29399             }
29400         }
29401         this.endUpdate();
29402     },
29403
29404     // private
29405     addTypedPanels : function(lr, ps){
29406         if(typeof ps == 'string'){
29407             lr.add(new Roo.ContentPanel(ps));
29408         }
29409         else if(ps instanceof Array){
29410             for(var i =0, len = ps.length; i < len; i++){
29411                 this.addTypedPanels(lr, ps[i]);
29412             }
29413         }
29414         else if(!ps.events){ // raw config?
29415             var el = ps.el;
29416             delete ps.el; // prevent conflict
29417             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29418         }
29419         else {  // panel object assumed!
29420             lr.add(ps);
29421         }
29422     },
29423     /**
29424      * Adds a xtype elements to the layout.
29425      * <pre><code>
29426
29427 layout.addxtype({
29428        xtype : 'ContentPanel',
29429        region: 'west',
29430        items: [ .... ]
29431    }
29432 );
29433
29434 layout.addxtype({
29435         xtype : 'NestedLayoutPanel',
29436         region: 'west',
29437         layout: {
29438            center: { },
29439            west: { }   
29440         },
29441         items : [ ... list of content panels or nested layout panels.. ]
29442    }
29443 );
29444 </code></pre>
29445      * @param {Object} cfg Xtype definition of item to add.
29446      */
29447     addxtype : function(cfg)
29448     {
29449         // basically accepts a pannel...
29450         // can accept a layout region..!?!?
29451         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
29452         
29453         if (!cfg.xtype.match(/Panel$/)) {
29454             return false;
29455         }
29456         var ret = false;
29457         
29458         if (typeof(cfg.region) == 'undefined') {
29459             Roo.log("Failed to add Panel, region was not set");
29460             Roo.log(cfg);
29461             return false;
29462         }
29463         var region = cfg.region;
29464         delete cfg.region;
29465         
29466           
29467         var xitems = [];
29468         if (cfg.items) {
29469             xitems = cfg.items;
29470             delete cfg.items;
29471         }
29472         var nb = false;
29473         
29474         switch(cfg.xtype) 
29475         {
29476             case 'ContentPanel':  // ContentPanel (el, cfg)
29477             case 'ScrollPanel':  // ContentPanel (el, cfg)
29478             case 'ViewPanel': 
29479                 if(cfg.autoCreate) {
29480                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29481                 } else {
29482                     var el = this.el.createChild();
29483                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29484                 }
29485                 
29486                 this.add(region, ret);
29487                 break;
29488             
29489             
29490             case 'TreePanel': // our new panel!
29491                 cfg.el = this.el.createChild();
29492                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29493                 this.add(region, ret);
29494                 break;
29495             
29496             case 'NestedLayoutPanel': 
29497                 // create a new Layout (which is  a Border Layout...
29498                 var el = this.el.createChild();
29499                 var clayout = cfg.layout;
29500                 delete cfg.layout;
29501                 clayout.items   = clayout.items  || [];
29502                 // replace this exitems with the clayout ones..
29503                 xitems = clayout.items;
29504                  
29505                 
29506                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29507                     cfg.background = false;
29508                 }
29509                 var layout = new Roo.BorderLayout(el, clayout);
29510                 
29511                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29512                 //console.log('adding nested layout panel '  + cfg.toSource());
29513                 this.add(region, ret);
29514                 nb = {}; /// find first...
29515                 break;
29516                 
29517             case 'GridPanel': 
29518             
29519                 // needs grid and region
29520                 
29521                 //var el = this.getRegion(region).el.createChild();
29522                 var el = this.el.createChild();
29523                 // create the grid first...
29524                 
29525                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29526                 delete cfg.grid;
29527                 if (region == 'center' && this.active ) {
29528                     cfg.background = false;
29529                 }
29530                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29531                 
29532                 this.add(region, ret);
29533                 if (cfg.background) {
29534                     ret.on('activate', function(gp) {
29535                         if (!gp.grid.rendered) {
29536                             gp.grid.render();
29537                         }
29538                     });
29539                 } else {
29540                     grid.render();
29541                 }
29542                 break;
29543            
29544            
29545            
29546                 
29547                 
29548                 
29549             default:
29550                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29551                     
29552                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29553                     this.add(region, ret);
29554                 } else {
29555                 
29556                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29557                     return null;
29558                 }
29559                 
29560              // GridPanel (grid, cfg)
29561             
29562         }
29563         this.beginUpdate();
29564         // add children..
29565         var region = '';
29566         var abn = {};
29567         Roo.each(xitems, function(i)  {
29568             region = nb && i.region ? i.region : false;
29569             
29570             var add = ret.addxtype(i);
29571            
29572             if (region) {
29573                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29574                 if (!i.background) {
29575                     abn[region] = nb[region] ;
29576                 }
29577             }
29578             
29579         });
29580         this.endUpdate();
29581
29582         // make the last non-background panel active..
29583         //if (nb) { Roo.log(abn); }
29584         if (nb) {
29585             
29586             for(var r in abn) {
29587                 region = this.getRegion(r);
29588                 if (region) {
29589                     // tried using nb[r], but it does not work..
29590                      
29591                     region.showPanel(abn[r]);
29592                    
29593                 }
29594             }
29595         }
29596         return ret;
29597         
29598     }
29599 });
29600
29601 /**
29602  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29603  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29604  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29605  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29606  * <pre><code>
29607 // shorthand
29608 var CP = Roo.ContentPanel;
29609
29610 var layout = Roo.BorderLayout.create({
29611     north: {
29612         initialSize: 25,
29613         titlebar: false,
29614         panels: [new CP("north", "North")]
29615     },
29616     west: {
29617         split:true,
29618         initialSize: 200,
29619         minSize: 175,
29620         maxSize: 400,
29621         titlebar: true,
29622         collapsible: true,
29623         panels: [new CP("west", {title: "West"})]
29624     },
29625     east: {
29626         split:true,
29627         initialSize: 202,
29628         minSize: 175,
29629         maxSize: 400,
29630         titlebar: true,
29631         collapsible: true,
29632         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29633     },
29634     south: {
29635         split:true,
29636         initialSize: 100,
29637         minSize: 100,
29638         maxSize: 200,
29639         titlebar: true,
29640         collapsible: true,
29641         panels: [new CP("south", {title: "South", closable: true})]
29642     },
29643     center: {
29644         titlebar: true,
29645         autoScroll:true,
29646         resizeTabs: true,
29647         minTabWidth: 50,
29648         preferredTabWidth: 150,
29649         panels: [
29650             new CP("center1", {title: "Close Me", closable: true}),
29651             new CP("center2", {title: "Center Panel", closable: false})
29652         ]
29653     }
29654 }, document.body);
29655
29656 layout.getRegion("center").showPanel("center1");
29657 </code></pre>
29658  * @param config
29659  * @param targetEl
29660  */
29661 Roo.BorderLayout.create = function(config, targetEl){
29662     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29663     layout.beginUpdate();
29664     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29665     for(var j = 0, jlen = regions.length; j < jlen; j++){
29666         var lr = regions[j];
29667         if(layout.regions[lr] && config[lr].panels){
29668             var r = layout.regions[lr];
29669             var ps = config[lr].panels;
29670             layout.addTypedPanels(r, ps);
29671         }
29672     }
29673     layout.endUpdate();
29674     return layout;
29675 };
29676
29677 // private
29678 Roo.BorderLayout.RegionFactory = {
29679     // private
29680     validRegions : ["north","south","east","west","center"],
29681
29682     // private
29683     create : function(target, mgr, config){
29684         target = target.toLowerCase();
29685         if(config.lightweight || config.basic){
29686             return new Roo.BasicLayoutRegion(mgr, config, target);
29687         }
29688         switch(target){
29689             case "north":
29690                 return new Roo.NorthLayoutRegion(mgr, config);
29691             case "south":
29692                 return new Roo.SouthLayoutRegion(mgr, config);
29693             case "east":
29694                 return new Roo.EastLayoutRegion(mgr, config);
29695             case "west":
29696                 return new Roo.WestLayoutRegion(mgr, config);
29697             case "center":
29698                 return new Roo.CenterLayoutRegion(mgr, config);
29699         }
29700         throw 'Layout region "'+target+'" not supported.';
29701     }
29702 };/*
29703  * Based on:
29704  * Ext JS Library 1.1.1
29705  * Copyright(c) 2006-2007, Ext JS, LLC.
29706  *
29707  * Originally Released Under LGPL - original licence link has changed is not relivant.
29708  *
29709  * Fork - LGPL
29710  * <script type="text/javascript">
29711  */
29712  
29713 /**
29714  * @class Roo.BasicLayoutRegion
29715  * @extends Roo.util.Observable
29716  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29717  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29718  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29719  */
29720 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29721     this.mgr = mgr;
29722     this.position  = pos;
29723     this.events = {
29724         /**
29725          * @scope Roo.BasicLayoutRegion
29726          */
29727         
29728         /**
29729          * @event beforeremove
29730          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29731          * @param {Roo.LayoutRegion} this
29732          * @param {Roo.ContentPanel} panel The panel
29733          * @param {Object} e The cancel event object
29734          */
29735         "beforeremove" : true,
29736         /**
29737          * @event invalidated
29738          * Fires when the layout for this region is changed.
29739          * @param {Roo.LayoutRegion} this
29740          */
29741         "invalidated" : true,
29742         /**
29743          * @event visibilitychange
29744          * Fires when this region is shown or hidden 
29745          * @param {Roo.LayoutRegion} this
29746          * @param {Boolean} visibility true or false
29747          */
29748         "visibilitychange" : true,
29749         /**
29750          * @event paneladded
29751          * Fires when a panel is added. 
29752          * @param {Roo.LayoutRegion} this
29753          * @param {Roo.ContentPanel} panel The panel
29754          */
29755         "paneladded" : true,
29756         /**
29757          * @event panelremoved
29758          * Fires when a panel is removed. 
29759          * @param {Roo.LayoutRegion} this
29760          * @param {Roo.ContentPanel} panel The panel
29761          */
29762         "panelremoved" : true,
29763         /**
29764          * @event beforecollapse
29765          * Fires when this region before collapse.
29766          * @param {Roo.LayoutRegion} this
29767          */
29768         "beforecollapse" : true,
29769         /**
29770          * @event collapsed
29771          * Fires when this region is collapsed.
29772          * @param {Roo.LayoutRegion} this
29773          */
29774         "collapsed" : true,
29775         /**
29776          * @event expanded
29777          * Fires when this region is expanded.
29778          * @param {Roo.LayoutRegion} this
29779          */
29780         "expanded" : true,
29781         /**
29782          * @event slideshow
29783          * Fires when this region is slid into view.
29784          * @param {Roo.LayoutRegion} this
29785          */
29786         "slideshow" : true,
29787         /**
29788          * @event slidehide
29789          * Fires when this region slides out of view. 
29790          * @param {Roo.LayoutRegion} this
29791          */
29792         "slidehide" : true,
29793         /**
29794          * @event panelactivated
29795          * Fires when a panel is activated. 
29796          * @param {Roo.LayoutRegion} this
29797          * @param {Roo.ContentPanel} panel The activated panel
29798          */
29799         "panelactivated" : true,
29800         /**
29801          * @event resized
29802          * Fires when the user resizes this region. 
29803          * @param {Roo.LayoutRegion} this
29804          * @param {Number} newSize The new size (width for east/west, height for north/south)
29805          */
29806         "resized" : true
29807     };
29808     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29809     this.panels = new Roo.util.MixedCollection();
29810     this.panels.getKey = this.getPanelId.createDelegate(this);
29811     this.box = null;
29812     this.activePanel = null;
29813     // ensure listeners are added...
29814     
29815     if (config.listeners || config.events) {
29816         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29817             listeners : config.listeners || {},
29818             events : config.events || {}
29819         });
29820     }
29821     
29822     if(skipConfig !== true){
29823         this.applyConfig(config);
29824     }
29825 };
29826
29827 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29828     getPanelId : function(p){
29829         return p.getId();
29830     },
29831     
29832     applyConfig : function(config){
29833         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29834         this.config = config;
29835         
29836     },
29837     
29838     /**
29839      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29840      * the width, for horizontal (north, south) the height.
29841      * @param {Number} newSize The new width or height
29842      */
29843     resizeTo : function(newSize){
29844         var el = this.el ? this.el :
29845                  (this.activePanel ? this.activePanel.getEl() : null);
29846         if(el){
29847             switch(this.position){
29848                 case "east":
29849                 case "west":
29850                     el.setWidth(newSize);
29851                     this.fireEvent("resized", this, newSize);
29852                 break;
29853                 case "north":
29854                 case "south":
29855                     el.setHeight(newSize);
29856                     this.fireEvent("resized", this, newSize);
29857                 break;                
29858             }
29859         }
29860     },
29861     
29862     getBox : function(){
29863         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29864     },
29865     
29866     getMargins : function(){
29867         return this.margins;
29868     },
29869     
29870     updateBox : function(box){
29871         this.box = box;
29872         var el = this.activePanel.getEl();
29873         el.dom.style.left = box.x + "px";
29874         el.dom.style.top = box.y + "px";
29875         this.activePanel.setSize(box.width, box.height);
29876     },
29877     
29878     /**
29879      * Returns the container element for this region.
29880      * @return {Roo.Element}
29881      */
29882     getEl : function(){
29883         return this.activePanel;
29884     },
29885     
29886     /**
29887      * Returns true if this region is currently visible.
29888      * @return {Boolean}
29889      */
29890     isVisible : function(){
29891         return this.activePanel ? true : false;
29892     },
29893     
29894     setActivePanel : function(panel){
29895         panel = this.getPanel(panel);
29896         if(this.activePanel && this.activePanel != panel){
29897             this.activePanel.setActiveState(false);
29898             this.activePanel.getEl().setLeftTop(-10000,-10000);
29899         }
29900         this.activePanel = panel;
29901         panel.setActiveState(true);
29902         if(this.box){
29903             panel.setSize(this.box.width, this.box.height);
29904         }
29905         this.fireEvent("panelactivated", this, panel);
29906         this.fireEvent("invalidated");
29907     },
29908     
29909     /**
29910      * Show the specified panel.
29911      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29912      * @return {Roo.ContentPanel} The shown panel or null
29913      */
29914     showPanel : function(panel){
29915         if(panel = this.getPanel(panel)){
29916             this.setActivePanel(panel);
29917         }
29918         return panel;
29919     },
29920     
29921     /**
29922      * Get the active panel for this region.
29923      * @return {Roo.ContentPanel} The active panel or null
29924      */
29925     getActivePanel : function(){
29926         return this.activePanel;
29927     },
29928     
29929     /**
29930      * Add the passed ContentPanel(s)
29931      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29932      * @return {Roo.ContentPanel} The panel added (if only one was added)
29933      */
29934     add : function(panel){
29935         if(arguments.length > 1){
29936             for(var i = 0, len = arguments.length; i < len; i++) {
29937                 this.add(arguments[i]);
29938             }
29939             return null;
29940         }
29941         if(this.hasPanel(panel)){
29942             this.showPanel(panel);
29943             return panel;
29944         }
29945         var el = panel.getEl();
29946         if(el.dom.parentNode != this.mgr.el.dom){
29947             this.mgr.el.dom.appendChild(el.dom);
29948         }
29949         if(panel.setRegion){
29950             panel.setRegion(this);
29951         }
29952         this.panels.add(panel);
29953         el.setStyle("position", "absolute");
29954         if(!panel.background){
29955             this.setActivePanel(panel);
29956             if(this.config.initialSize && this.panels.getCount()==1){
29957                 this.resizeTo(this.config.initialSize);
29958             }
29959         }
29960         this.fireEvent("paneladded", this, panel);
29961         return panel;
29962     },
29963     
29964     /**
29965      * Returns true if the panel is in this region.
29966      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29967      * @return {Boolean}
29968      */
29969     hasPanel : function(panel){
29970         if(typeof panel == "object"){ // must be panel obj
29971             panel = panel.getId();
29972         }
29973         return this.getPanel(panel) ? true : false;
29974     },
29975     
29976     /**
29977      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29978      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29979      * @param {Boolean} preservePanel Overrides the config preservePanel option
29980      * @return {Roo.ContentPanel} The panel that was removed
29981      */
29982     remove : function(panel, preservePanel){
29983         panel = this.getPanel(panel);
29984         if(!panel){
29985             return null;
29986         }
29987         var e = {};
29988         this.fireEvent("beforeremove", this, panel, e);
29989         if(e.cancel === true){
29990             return null;
29991         }
29992         var panelId = panel.getId();
29993         this.panels.removeKey(panelId);
29994         return panel;
29995     },
29996     
29997     /**
29998      * Returns the panel specified or null if it's not in this region.
29999      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30000      * @return {Roo.ContentPanel}
30001      */
30002     getPanel : function(id){
30003         if(typeof id == "object"){ // must be panel obj
30004             return id;
30005         }
30006         return this.panels.get(id);
30007     },
30008     
30009     /**
30010      * Returns this regions position (north/south/east/west/center).
30011      * @return {String} 
30012      */
30013     getPosition: function(){
30014         return this.position;    
30015     }
30016 });/*
30017  * Based on:
30018  * Ext JS Library 1.1.1
30019  * Copyright(c) 2006-2007, Ext JS, LLC.
30020  *
30021  * Originally Released Under LGPL - original licence link has changed is not relivant.
30022  *
30023  * Fork - LGPL
30024  * <script type="text/javascript">
30025  */
30026  
30027 /**
30028  * @class Roo.LayoutRegion
30029  * @extends Roo.BasicLayoutRegion
30030  * This class represents a region in a layout manager.
30031  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
30032  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
30033  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
30034  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
30035  * @cfg {Object}    cmargins        Margins for the element when collapsed (defaults to: north/south {top: 2, left: 0, right:0, bottom: 2} or east/west {top: 0, left: 2, right:2, bottom: 0})
30036  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
30037  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
30038  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
30039  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
30040  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
30041  * @cfg {String}    title           The title for the region (overrides panel titles)
30042  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
30043  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
30044  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
30045  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
30046  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
30047  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
30048  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
30049  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
30050  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
30051  * @cfg {Boolean}   showPin         True to show a pin button
30052  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
30053  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
30054  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
30055  * @cfg {Number}    width           For East/West panels
30056  * @cfg {Number}    height          For North/South panels
30057  * @cfg {Boolean}   split           To show the splitter
30058  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
30059  */
30060 Roo.LayoutRegion = function(mgr, config, pos){
30061     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
30062     var dh = Roo.DomHelper;
30063     /** This region's container element 
30064     * @type Roo.Element */
30065     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
30066     /** This region's title element 
30067     * @type Roo.Element */
30068
30069     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
30070         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
30071         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
30072     ]}, true);
30073     this.titleEl.enableDisplayMode();
30074     /** This region's title text element 
30075     * @type HTMLElement */
30076     this.titleTextEl = this.titleEl.dom.firstChild;
30077     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
30078     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
30079     this.closeBtn.enableDisplayMode();
30080     this.closeBtn.on("click", this.closeClicked, this);
30081     this.closeBtn.hide();
30082
30083     this.createBody(config);
30084     this.visible = true;
30085     this.collapsed = false;
30086
30087     if(config.hideWhenEmpty){
30088         this.hide();
30089         this.on("paneladded", this.validateVisibility, this);
30090         this.on("panelremoved", this.validateVisibility, this);
30091     }
30092     this.applyConfig(config);
30093 };
30094
30095 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
30096
30097     createBody : function(){
30098         /** This region's body element 
30099         * @type Roo.Element */
30100         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30101     },
30102
30103     applyConfig : function(c){
30104         if(c.collapsible && this.position != "center" && !this.collapsedEl){
30105             var dh = Roo.DomHelper;
30106             if(c.titlebar !== false){
30107                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30108                 this.collapseBtn.on("click", this.collapse, this);
30109                 this.collapseBtn.enableDisplayMode();
30110
30111                 if(c.showPin === true || this.showPin){
30112                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30113                     this.stickBtn.enableDisplayMode();
30114                     this.stickBtn.on("click", this.expand, this);
30115                     this.stickBtn.hide();
30116                 }
30117             }
30118             /** This region's collapsed element
30119             * @type Roo.Element */
30120             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30121                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30122             ]}, true);
30123             if(c.floatable !== false){
30124                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30125                this.collapsedEl.on("click", this.collapseClick, this);
30126             }
30127
30128             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30129                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30130                    id: "message", unselectable: "on", style:{"float":"left"}});
30131                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30132              }
30133             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30134             this.expandBtn.on("click", this.expand, this);
30135         }
30136         if(this.collapseBtn){
30137             this.collapseBtn.setVisible(c.collapsible == true);
30138         }
30139         this.cmargins = c.cmargins || this.cmargins ||
30140                          (this.position == "west" || this.position == "east" ?
30141                              {top: 0, left: 2, right:2, bottom: 0} :
30142                              {top: 2, left: 0, right:0, bottom: 2});
30143         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30144         this.bottomTabs = c.tabPosition != "top";
30145         this.autoScroll = c.autoScroll || false;
30146         if(this.autoScroll){
30147             this.bodyEl.setStyle("overflow", "auto");
30148         }else{
30149             this.bodyEl.setStyle("overflow", "hidden");
30150         }
30151         //if(c.titlebar !== false){
30152             if((!c.titlebar && !c.title) || c.titlebar === false){
30153                 this.titleEl.hide();
30154             }else{
30155                 this.titleEl.show();
30156                 if(c.title){
30157                     this.titleTextEl.innerHTML = c.title;
30158                 }
30159             }
30160         //}
30161         this.duration = c.duration || .30;
30162         this.slideDuration = c.slideDuration || .45;
30163         this.config = c;
30164         if(c.collapsed){
30165             this.collapse(true);
30166         }
30167         if(c.hidden){
30168             this.hide();
30169         }
30170     },
30171     /**
30172      * Returns true if this region is currently visible.
30173      * @return {Boolean}
30174      */
30175     isVisible : function(){
30176         return this.visible;
30177     },
30178
30179     /**
30180      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30181      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
30182      */
30183     setCollapsedTitle : function(title){
30184         title = title || "&#160;";
30185         if(this.collapsedTitleTextEl){
30186             this.collapsedTitleTextEl.innerHTML = title;
30187         }
30188     },
30189
30190     getBox : function(){
30191         var b;
30192         if(!this.collapsed){
30193             b = this.el.getBox(false, true);
30194         }else{
30195             b = this.collapsedEl.getBox(false, true);
30196         }
30197         return b;
30198     },
30199
30200     getMargins : function(){
30201         return this.collapsed ? this.cmargins : this.margins;
30202     },
30203
30204     highlight : function(){
30205         this.el.addClass("x-layout-panel-dragover");
30206     },
30207
30208     unhighlight : function(){
30209         this.el.removeClass("x-layout-panel-dragover");
30210     },
30211
30212     updateBox : function(box){
30213         this.box = box;
30214         if(!this.collapsed){
30215             this.el.dom.style.left = box.x + "px";
30216             this.el.dom.style.top = box.y + "px";
30217             this.updateBody(box.width, box.height);
30218         }else{
30219             this.collapsedEl.dom.style.left = box.x + "px";
30220             this.collapsedEl.dom.style.top = box.y + "px";
30221             this.collapsedEl.setSize(box.width, box.height);
30222         }
30223         if(this.tabs){
30224             this.tabs.autoSizeTabs();
30225         }
30226     },
30227
30228     updateBody : function(w, h){
30229         if(w !== null){
30230             this.el.setWidth(w);
30231             w -= this.el.getBorderWidth("rl");
30232             if(this.config.adjustments){
30233                 w += this.config.adjustments[0];
30234             }
30235         }
30236         if(h !== null){
30237             this.el.setHeight(h);
30238             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
30239             h -= this.el.getBorderWidth("tb");
30240             if(this.config.adjustments){
30241                 h += this.config.adjustments[1];
30242             }
30243             this.bodyEl.setHeight(h);
30244             if(this.tabs){
30245                 h = this.tabs.syncHeight(h);
30246             }
30247         }
30248         if(this.panelSize){
30249             w = w !== null ? w : this.panelSize.width;
30250             h = h !== null ? h : this.panelSize.height;
30251         }
30252         if(this.activePanel){
30253             var el = this.activePanel.getEl();
30254             w = w !== null ? w : el.getWidth();
30255             h = h !== null ? h : el.getHeight();
30256             this.panelSize = {width: w, height: h};
30257             this.activePanel.setSize(w, h);
30258         }
30259         if(Roo.isIE && this.tabs){
30260             this.tabs.el.repaint();
30261         }
30262     },
30263
30264     /**
30265      * Returns the container element for this region.
30266      * @return {Roo.Element}
30267      */
30268     getEl : function(){
30269         return this.el;
30270     },
30271
30272     /**
30273      * Hides this region.
30274      */
30275     hide : function(){
30276         if(!this.collapsed){
30277             this.el.dom.style.left = "-2000px";
30278             this.el.hide();
30279         }else{
30280             this.collapsedEl.dom.style.left = "-2000px";
30281             this.collapsedEl.hide();
30282         }
30283         this.visible = false;
30284         this.fireEvent("visibilitychange", this, false);
30285     },
30286
30287     /**
30288      * Shows this region if it was previously hidden.
30289      */
30290     show : function(){
30291         if(!this.collapsed){
30292             this.el.show();
30293         }else{
30294             this.collapsedEl.show();
30295         }
30296         this.visible = true;
30297         this.fireEvent("visibilitychange", this, true);
30298     },
30299
30300     closeClicked : function(){
30301         if(this.activePanel){
30302             this.remove(this.activePanel);
30303         }
30304     },
30305
30306     collapseClick : function(e){
30307         if(this.isSlid){
30308            e.stopPropagation();
30309            this.slideIn();
30310         }else{
30311            e.stopPropagation();
30312            this.slideOut();
30313         }
30314     },
30315
30316     /**
30317      * Collapses this region.
30318      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30319      */
30320     collapse : function(skipAnim, skipCheck){
30321         if(this.collapsed) {
30322             return;
30323         }
30324         
30325         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
30326             
30327             this.collapsed = true;
30328             if(this.split){
30329                 this.split.el.hide();
30330             }
30331             if(this.config.animate && skipAnim !== true){
30332                 this.fireEvent("invalidated", this);
30333                 this.animateCollapse();
30334             }else{
30335                 this.el.setLocation(-20000,-20000);
30336                 this.el.hide();
30337                 this.collapsedEl.show();
30338                 this.fireEvent("collapsed", this);
30339                 this.fireEvent("invalidated", this);
30340             }
30341         }
30342         
30343     },
30344
30345     animateCollapse : function(){
30346         // overridden
30347     },
30348
30349     /**
30350      * Expands this region if it was previously collapsed.
30351      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30352      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30353      */
30354     expand : function(e, skipAnim){
30355         if(e) {
30356             e.stopPropagation();
30357         }
30358         if(!this.collapsed || this.el.hasActiveFx()) {
30359             return;
30360         }
30361         if(this.isSlid){
30362             this.afterSlideIn();
30363             skipAnim = true;
30364         }
30365         this.collapsed = false;
30366         if(this.config.animate && skipAnim !== true){
30367             this.animateExpand();
30368         }else{
30369             this.el.show();
30370             if(this.split){
30371                 this.split.el.show();
30372             }
30373             this.collapsedEl.setLocation(-2000,-2000);
30374             this.collapsedEl.hide();
30375             this.fireEvent("invalidated", this);
30376             this.fireEvent("expanded", this);
30377         }
30378     },
30379
30380     animateExpand : function(){
30381         // overridden
30382     },
30383
30384     initTabs : function()
30385     {
30386         this.bodyEl.setStyle("overflow", "hidden");
30387         var ts = new Roo.TabPanel(
30388                 this.bodyEl.dom,
30389                 {
30390                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
30391                     disableTooltips: this.config.disableTabTips,
30392                     toolbar : this.config.toolbar
30393                 }
30394         );
30395         if(this.config.hideTabs){
30396             ts.stripWrap.setDisplayed(false);
30397         }
30398         this.tabs = ts;
30399         ts.resizeTabs = this.config.resizeTabs === true;
30400         ts.minTabWidth = this.config.minTabWidth || 40;
30401         ts.maxTabWidth = this.config.maxTabWidth || 250;
30402         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30403         ts.monitorResize = false;
30404         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30405         ts.bodyEl.addClass('x-layout-tabs-body');
30406         this.panels.each(this.initPanelAsTab, this);
30407     },
30408
30409     initPanelAsTab : function(panel){
30410         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30411                     this.config.closeOnTab && panel.isClosable());
30412         if(panel.tabTip !== undefined){
30413             ti.setTooltip(panel.tabTip);
30414         }
30415         ti.on("activate", function(){
30416               this.setActivePanel(panel);
30417         }, this);
30418         if(this.config.closeOnTab){
30419             ti.on("beforeclose", function(t, e){
30420                 e.cancel = true;
30421                 this.remove(panel);
30422             }, this);
30423         }
30424         return ti;
30425     },
30426
30427     updatePanelTitle : function(panel, title){
30428         if(this.activePanel == panel){
30429             this.updateTitle(title);
30430         }
30431         if(this.tabs){
30432             var ti = this.tabs.getTab(panel.getEl().id);
30433             ti.setText(title);
30434             if(panel.tabTip !== undefined){
30435                 ti.setTooltip(panel.tabTip);
30436             }
30437         }
30438     },
30439
30440     updateTitle : function(title){
30441         if(this.titleTextEl && !this.config.title){
30442             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30443         }
30444     },
30445
30446     setActivePanel : function(panel){
30447         panel = this.getPanel(panel);
30448         if(this.activePanel && this.activePanel != panel){
30449             this.activePanel.setActiveState(false);
30450         }
30451         this.activePanel = panel;
30452         panel.setActiveState(true);
30453         if(this.panelSize){
30454             panel.setSize(this.panelSize.width, this.panelSize.height);
30455         }
30456         if(this.closeBtn){
30457             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30458         }
30459         this.updateTitle(panel.getTitle());
30460         if(this.tabs){
30461             this.fireEvent("invalidated", this);
30462         }
30463         this.fireEvent("panelactivated", this, panel);
30464     },
30465
30466     /**
30467      * Shows the specified panel.
30468      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30469      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30470      */
30471     showPanel : function(panel)
30472     {
30473         panel = this.getPanel(panel);
30474         if(panel){
30475             if(this.tabs){
30476                 var tab = this.tabs.getTab(panel.getEl().id);
30477                 if(tab.isHidden()){
30478                     this.tabs.unhideTab(tab.id);
30479                 }
30480                 tab.activate();
30481             }else{
30482                 this.setActivePanel(panel);
30483             }
30484         }
30485         return panel;
30486     },
30487
30488     /**
30489      * Get the active panel for this region.
30490      * @return {Roo.ContentPanel} The active panel or null
30491      */
30492     getActivePanel : function(){
30493         return this.activePanel;
30494     },
30495
30496     validateVisibility : function(){
30497         if(this.panels.getCount() < 1){
30498             this.updateTitle("&#160;");
30499             this.closeBtn.hide();
30500             this.hide();
30501         }else{
30502             if(!this.isVisible()){
30503                 this.show();
30504             }
30505         }
30506     },
30507
30508     /**
30509      * Adds the passed ContentPanel(s) to this region.
30510      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30511      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30512      */
30513     add : function(panel){
30514         if(arguments.length > 1){
30515             for(var i = 0, len = arguments.length; i < len; i++) {
30516                 this.add(arguments[i]);
30517             }
30518             return null;
30519         }
30520         if(this.hasPanel(panel)){
30521             this.showPanel(panel);
30522             return panel;
30523         }
30524         panel.setRegion(this);
30525         this.panels.add(panel);
30526         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30527             this.bodyEl.dom.appendChild(panel.getEl().dom);
30528             if(panel.background !== true){
30529                 this.setActivePanel(panel);
30530             }
30531             this.fireEvent("paneladded", this, panel);
30532             return panel;
30533         }
30534         if(!this.tabs){
30535             this.initTabs();
30536         }else{
30537             this.initPanelAsTab(panel);
30538         }
30539         if(panel.background !== true){
30540             this.tabs.activate(panel.getEl().id);
30541         }
30542         this.fireEvent("paneladded", this, panel);
30543         return panel;
30544     },
30545
30546     /**
30547      * Hides the tab for the specified panel.
30548      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30549      */
30550     hidePanel : function(panel){
30551         if(this.tabs && (panel = this.getPanel(panel))){
30552             this.tabs.hideTab(panel.getEl().id);
30553         }
30554     },
30555
30556     /**
30557      * Unhides the tab for a previously hidden panel.
30558      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30559      */
30560     unhidePanel : function(panel){
30561         if(this.tabs && (panel = this.getPanel(panel))){
30562             this.tabs.unhideTab(panel.getEl().id);
30563         }
30564     },
30565
30566     clearPanels : function(){
30567         while(this.panels.getCount() > 0){
30568              this.remove(this.panels.first());
30569         }
30570     },
30571
30572     /**
30573      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30574      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30575      * @param {Boolean} preservePanel Overrides the config preservePanel option
30576      * @return {Roo.ContentPanel} The panel that was removed
30577      */
30578     remove : function(panel, preservePanel){
30579         panel = this.getPanel(panel);
30580         if(!panel){
30581             return null;
30582         }
30583         var e = {};
30584         this.fireEvent("beforeremove", this, panel, e);
30585         if(e.cancel === true){
30586             return null;
30587         }
30588         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30589         var panelId = panel.getId();
30590         this.panels.removeKey(panelId);
30591         if(preservePanel){
30592             document.body.appendChild(panel.getEl().dom);
30593         }
30594         if(this.tabs){
30595             this.tabs.removeTab(panel.getEl().id);
30596         }else if (!preservePanel){
30597             this.bodyEl.dom.removeChild(panel.getEl().dom);
30598         }
30599         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30600             var p = this.panels.first();
30601             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30602             tempEl.appendChild(p.getEl().dom);
30603             this.bodyEl.update("");
30604             this.bodyEl.dom.appendChild(p.getEl().dom);
30605             tempEl = null;
30606             this.updateTitle(p.getTitle());
30607             this.tabs = null;
30608             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30609             this.setActivePanel(p);
30610         }
30611         panel.setRegion(null);
30612         if(this.activePanel == panel){
30613             this.activePanel = null;
30614         }
30615         if(this.config.autoDestroy !== false && preservePanel !== true){
30616             try{panel.destroy();}catch(e){}
30617         }
30618         this.fireEvent("panelremoved", this, panel);
30619         return panel;
30620     },
30621
30622     /**
30623      * Returns the TabPanel component used by this region
30624      * @return {Roo.TabPanel}
30625      */
30626     getTabs : function(){
30627         return this.tabs;
30628     },
30629
30630     createTool : function(parentEl, className){
30631         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30632             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30633         btn.addClassOnOver("x-layout-tools-button-over");
30634         return btn;
30635     }
30636 });/*
30637  * Based on:
30638  * Ext JS Library 1.1.1
30639  * Copyright(c) 2006-2007, Ext JS, LLC.
30640  *
30641  * Originally Released Under LGPL - original licence link has changed is not relivant.
30642  *
30643  * Fork - LGPL
30644  * <script type="text/javascript">
30645  */
30646  
30647
30648
30649 /**
30650  * @class Roo.SplitLayoutRegion
30651  * @extends Roo.LayoutRegion
30652  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30653  */
30654 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30655     this.cursor = cursor;
30656     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30657 };
30658
30659 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30660     splitTip : "Drag to resize.",
30661     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30662     useSplitTips : false,
30663
30664     applyConfig : function(config){
30665         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30666         if(config.split){
30667             if(!this.split){
30668                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30669                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30670                 /** The SplitBar for this region 
30671                 * @type Roo.SplitBar */
30672                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30673                 this.split.on("moved", this.onSplitMove, this);
30674                 this.split.useShim = config.useShim === true;
30675                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30676                 if(this.useSplitTips){
30677                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30678                 }
30679                 if(config.collapsible){
30680                     this.split.el.on("dblclick", this.collapse,  this);
30681                 }
30682             }
30683             if(typeof config.minSize != "undefined"){
30684                 this.split.minSize = config.minSize;
30685             }
30686             if(typeof config.maxSize != "undefined"){
30687                 this.split.maxSize = config.maxSize;
30688             }
30689             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30690                 this.hideSplitter();
30691             }
30692         }
30693     },
30694
30695     getHMaxSize : function(){
30696          var cmax = this.config.maxSize || 10000;
30697          var center = this.mgr.getRegion("center");
30698          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30699     },
30700
30701     getVMaxSize : function(){
30702          var cmax = this.config.maxSize || 10000;
30703          var center = this.mgr.getRegion("center");
30704          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30705     },
30706
30707     onSplitMove : function(split, newSize){
30708         this.fireEvent("resized", this, newSize);
30709     },
30710     
30711     /** 
30712      * Returns the {@link Roo.SplitBar} for this region.
30713      * @return {Roo.SplitBar}
30714      */
30715     getSplitBar : function(){
30716         return this.split;
30717     },
30718     
30719     hide : function(){
30720         this.hideSplitter();
30721         Roo.SplitLayoutRegion.superclass.hide.call(this);
30722     },
30723
30724     hideSplitter : function(){
30725         if(this.split){
30726             this.split.el.setLocation(-2000,-2000);
30727             this.split.el.hide();
30728         }
30729     },
30730
30731     show : function(){
30732         if(this.split){
30733             this.split.el.show();
30734         }
30735         Roo.SplitLayoutRegion.superclass.show.call(this);
30736     },
30737     
30738     beforeSlide: function(){
30739         if(Roo.isGecko){// firefox overflow auto bug workaround
30740             this.bodyEl.clip();
30741             if(this.tabs) {
30742                 this.tabs.bodyEl.clip();
30743             }
30744             if(this.activePanel){
30745                 this.activePanel.getEl().clip();
30746                 
30747                 if(this.activePanel.beforeSlide){
30748                     this.activePanel.beforeSlide();
30749                 }
30750             }
30751         }
30752     },
30753     
30754     afterSlide : function(){
30755         if(Roo.isGecko){// firefox overflow auto bug workaround
30756             this.bodyEl.unclip();
30757             if(this.tabs) {
30758                 this.tabs.bodyEl.unclip();
30759             }
30760             if(this.activePanel){
30761                 this.activePanel.getEl().unclip();
30762                 if(this.activePanel.afterSlide){
30763                     this.activePanel.afterSlide();
30764                 }
30765             }
30766         }
30767     },
30768
30769     initAutoHide : function(){
30770         if(this.autoHide !== false){
30771             if(!this.autoHideHd){
30772                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30773                 this.autoHideHd = {
30774                     "mouseout": function(e){
30775                         if(!e.within(this.el, true)){
30776                             st.delay(500);
30777                         }
30778                     },
30779                     "mouseover" : function(e){
30780                         st.cancel();
30781                     },
30782                     scope : this
30783                 };
30784             }
30785             this.el.on(this.autoHideHd);
30786         }
30787     },
30788
30789     clearAutoHide : function(){
30790         if(this.autoHide !== false){
30791             this.el.un("mouseout", this.autoHideHd.mouseout);
30792             this.el.un("mouseover", this.autoHideHd.mouseover);
30793         }
30794     },
30795
30796     clearMonitor : function(){
30797         Roo.get(document).un("click", this.slideInIf, this);
30798     },
30799
30800     // these names are backwards but not changed for compat
30801     slideOut : function(){
30802         if(this.isSlid || this.el.hasActiveFx()){
30803             return;
30804         }
30805         this.isSlid = true;
30806         if(this.collapseBtn){
30807             this.collapseBtn.hide();
30808         }
30809         this.closeBtnState = this.closeBtn.getStyle('display');
30810         this.closeBtn.hide();
30811         if(this.stickBtn){
30812             this.stickBtn.show();
30813         }
30814         this.el.show();
30815         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30816         this.beforeSlide();
30817         this.el.setStyle("z-index", 10001);
30818         this.el.slideIn(this.getSlideAnchor(), {
30819             callback: function(){
30820                 this.afterSlide();
30821                 this.initAutoHide();
30822                 Roo.get(document).on("click", this.slideInIf, this);
30823                 this.fireEvent("slideshow", this);
30824             },
30825             scope: this,
30826             block: true
30827         });
30828     },
30829
30830     afterSlideIn : function(){
30831         this.clearAutoHide();
30832         this.isSlid = false;
30833         this.clearMonitor();
30834         this.el.setStyle("z-index", "");
30835         if(this.collapseBtn){
30836             this.collapseBtn.show();
30837         }
30838         this.closeBtn.setStyle('display', this.closeBtnState);
30839         if(this.stickBtn){
30840             this.stickBtn.hide();
30841         }
30842         this.fireEvent("slidehide", this);
30843     },
30844
30845     slideIn : function(cb){
30846         if(!this.isSlid || this.el.hasActiveFx()){
30847             Roo.callback(cb);
30848             return;
30849         }
30850         this.isSlid = false;
30851         this.beforeSlide();
30852         this.el.slideOut(this.getSlideAnchor(), {
30853             callback: function(){
30854                 this.el.setLeftTop(-10000, -10000);
30855                 this.afterSlide();
30856                 this.afterSlideIn();
30857                 Roo.callback(cb);
30858             },
30859             scope: this,
30860             block: true
30861         });
30862     },
30863     
30864     slideInIf : function(e){
30865         if(!e.within(this.el)){
30866             this.slideIn();
30867         }
30868     },
30869
30870     animateCollapse : function(){
30871         this.beforeSlide();
30872         this.el.setStyle("z-index", 20000);
30873         var anchor = this.getSlideAnchor();
30874         this.el.slideOut(anchor, {
30875             callback : function(){
30876                 this.el.setStyle("z-index", "");
30877                 this.collapsedEl.slideIn(anchor, {duration:.3});
30878                 this.afterSlide();
30879                 this.el.setLocation(-10000,-10000);
30880                 this.el.hide();
30881                 this.fireEvent("collapsed", this);
30882             },
30883             scope: this,
30884             block: true
30885         });
30886     },
30887
30888     animateExpand : function(){
30889         this.beforeSlide();
30890         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30891         this.el.setStyle("z-index", 20000);
30892         this.collapsedEl.hide({
30893             duration:.1
30894         });
30895         this.el.slideIn(this.getSlideAnchor(), {
30896             callback : function(){
30897                 this.el.setStyle("z-index", "");
30898                 this.afterSlide();
30899                 if(this.split){
30900                     this.split.el.show();
30901                 }
30902                 this.fireEvent("invalidated", this);
30903                 this.fireEvent("expanded", this);
30904             },
30905             scope: this,
30906             block: true
30907         });
30908     },
30909
30910     anchors : {
30911         "west" : "left",
30912         "east" : "right",
30913         "north" : "top",
30914         "south" : "bottom"
30915     },
30916
30917     sanchors : {
30918         "west" : "l",
30919         "east" : "r",
30920         "north" : "t",
30921         "south" : "b"
30922     },
30923
30924     canchors : {
30925         "west" : "tl-tr",
30926         "east" : "tr-tl",
30927         "north" : "tl-bl",
30928         "south" : "bl-tl"
30929     },
30930
30931     getAnchor : function(){
30932         return this.anchors[this.position];
30933     },
30934
30935     getCollapseAnchor : function(){
30936         return this.canchors[this.position];
30937     },
30938
30939     getSlideAnchor : function(){
30940         return this.sanchors[this.position];
30941     },
30942
30943     getAlignAdj : function(){
30944         var cm = this.cmargins;
30945         switch(this.position){
30946             case "west":
30947                 return [0, 0];
30948             break;
30949             case "east":
30950                 return [0, 0];
30951             break;
30952             case "north":
30953                 return [0, 0];
30954             break;
30955             case "south":
30956                 return [0, 0];
30957             break;
30958         }
30959     },
30960
30961     getExpandAdj : function(){
30962         var c = this.collapsedEl, cm = this.cmargins;
30963         switch(this.position){
30964             case "west":
30965                 return [-(cm.right+c.getWidth()+cm.left), 0];
30966             break;
30967             case "east":
30968                 return [cm.right+c.getWidth()+cm.left, 0];
30969             break;
30970             case "north":
30971                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30972             break;
30973             case "south":
30974                 return [0, cm.top+cm.bottom+c.getHeight()];
30975             break;
30976         }
30977     }
30978 });/*
30979  * Based on:
30980  * Ext JS Library 1.1.1
30981  * Copyright(c) 2006-2007, Ext JS, LLC.
30982  *
30983  * Originally Released Under LGPL - original licence link has changed is not relivant.
30984  *
30985  * Fork - LGPL
30986  * <script type="text/javascript">
30987  */
30988 /*
30989  * These classes are private internal classes
30990  */
30991 Roo.CenterLayoutRegion = function(mgr, config){
30992     Roo.LayoutRegion.call(this, mgr, config, "center");
30993     this.visible = true;
30994     this.minWidth = config.minWidth || 20;
30995     this.minHeight = config.minHeight || 20;
30996 };
30997
30998 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30999     hide : function(){
31000         // center panel can't be hidden
31001     },
31002     
31003     show : function(){
31004         // center panel can't be hidden
31005     },
31006     
31007     getMinWidth: function(){
31008         return this.minWidth;
31009     },
31010     
31011     getMinHeight: function(){
31012         return this.minHeight;
31013     }
31014 });
31015
31016
31017 Roo.NorthLayoutRegion = function(mgr, config){
31018     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
31019     if(this.split){
31020         this.split.placement = Roo.SplitBar.TOP;
31021         this.split.orientation = Roo.SplitBar.VERTICAL;
31022         this.split.el.addClass("x-layout-split-v");
31023     }
31024     var size = config.initialSize || config.height;
31025     if(typeof size != "undefined"){
31026         this.el.setHeight(size);
31027     }
31028 };
31029 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
31030     orientation: Roo.SplitBar.VERTICAL,
31031     getBox : function(){
31032         if(this.collapsed){
31033             return this.collapsedEl.getBox();
31034         }
31035         var box = this.el.getBox();
31036         if(this.split){
31037             box.height += this.split.el.getHeight();
31038         }
31039         return box;
31040     },
31041     
31042     updateBox : function(box){
31043         if(this.split && !this.collapsed){
31044             box.height -= this.split.el.getHeight();
31045             this.split.el.setLeft(box.x);
31046             this.split.el.setTop(box.y+box.height);
31047             this.split.el.setWidth(box.width);
31048         }
31049         if(this.collapsed){
31050             this.updateBody(box.width, null);
31051         }
31052         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31053     }
31054 });
31055
31056 Roo.SouthLayoutRegion = function(mgr, config){
31057     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
31058     if(this.split){
31059         this.split.placement = Roo.SplitBar.BOTTOM;
31060         this.split.orientation = Roo.SplitBar.VERTICAL;
31061         this.split.el.addClass("x-layout-split-v");
31062     }
31063     var size = config.initialSize || config.height;
31064     if(typeof size != "undefined"){
31065         this.el.setHeight(size);
31066     }
31067 };
31068 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
31069     orientation: Roo.SplitBar.VERTICAL,
31070     getBox : function(){
31071         if(this.collapsed){
31072             return this.collapsedEl.getBox();
31073         }
31074         var box = this.el.getBox();
31075         if(this.split){
31076             var sh = this.split.el.getHeight();
31077             box.height += sh;
31078             box.y -= sh;
31079         }
31080         return box;
31081     },
31082     
31083     updateBox : function(box){
31084         if(this.split && !this.collapsed){
31085             var sh = this.split.el.getHeight();
31086             box.height -= sh;
31087             box.y += sh;
31088             this.split.el.setLeft(box.x);
31089             this.split.el.setTop(box.y-sh);
31090             this.split.el.setWidth(box.width);
31091         }
31092         if(this.collapsed){
31093             this.updateBody(box.width, null);
31094         }
31095         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31096     }
31097 });
31098
31099 Roo.EastLayoutRegion = function(mgr, config){
31100     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31101     if(this.split){
31102         this.split.placement = Roo.SplitBar.RIGHT;
31103         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31104         this.split.el.addClass("x-layout-split-h");
31105     }
31106     var size = config.initialSize || config.width;
31107     if(typeof size != "undefined"){
31108         this.el.setWidth(size);
31109     }
31110 };
31111 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31112     orientation: Roo.SplitBar.HORIZONTAL,
31113     getBox : function(){
31114         if(this.collapsed){
31115             return this.collapsedEl.getBox();
31116         }
31117         var box = this.el.getBox();
31118         if(this.split){
31119             var sw = this.split.el.getWidth();
31120             box.width += sw;
31121             box.x -= sw;
31122         }
31123         return box;
31124     },
31125
31126     updateBox : function(box){
31127         if(this.split && !this.collapsed){
31128             var sw = this.split.el.getWidth();
31129             box.width -= sw;
31130             this.split.el.setLeft(box.x);
31131             this.split.el.setTop(box.y);
31132             this.split.el.setHeight(box.height);
31133             box.x += sw;
31134         }
31135         if(this.collapsed){
31136             this.updateBody(null, box.height);
31137         }
31138         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31139     }
31140 });
31141
31142 Roo.WestLayoutRegion = function(mgr, config){
31143     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31144     if(this.split){
31145         this.split.placement = Roo.SplitBar.LEFT;
31146         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31147         this.split.el.addClass("x-layout-split-h");
31148     }
31149     var size = config.initialSize || config.width;
31150     if(typeof size != "undefined"){
31151         this.el.setWidth(size);
31152     }
31153 };
31154 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31155     orientation: Roo.SplitBar.HORIZONTAL,
31156     getBox : function(){
31157         if(this.collapsed){
31158             return this.collapsedEl.getBox();
31159         }
31160         var box = this.el.getBox();
31161         if(this.split){
31162             box.width += this.split.el.getWidth();
31163         }
31164         return box;
31165     },
31166     
31167     updateBox : function(box){
31168         if(this.split && !this.collapsed){
31169             var sw = this.split.el.getWidth();
31170             box.width -= sw;
31171             this.split.el.setLeft(box.x+box.width);
31172             this.split.el.setTop(box.y);
31173             this.split.el.setHeight(box.height);
31174         }
31175         if(this.collapsed){
31176             this.updateBody(null, box.height);
31177         }
31178         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31179     }
31180 });
31181 /*
31182  * Based on:
31183  * Ext JS Library 1.1.1
31184  * Copyright(c) 2006-2007, Ext JS, LLC.
31185  *
31186  * Originally Released Under LGPL - original licence link has changed is not relivant.
31187  *
31188  * Fork - LGPL
31189  * <script type="text/javascript">
31190  */
31191  
31192  
31193 /*
31194  * Private internal class for reading and applying state
31195  */
31196 Roo.LayoutStateManager = function(layout){
31197      // default empty state
31198      this.state = {
31199         north: {},
31200         south: {},
31201         east: {},
31202         west: {}       
31203     };
31204 };
31205
31206 Roo.LayoutStateManager.prototype = {
31207     init : function(layout, provider){
31208         this.provider = provider;
31209         var state = provider.get(layout.id+"-layout-state");
31210         if(state){
31211             var wasUpdating = layout.isUpdating();
31212             if(!wasUpdating){
31213                 layout.beginUpdate();
31214             }
31215             for(var key in state){
31216                 if(typeof state[key] != "function"){
31217                     var rstate = state[key];
31218                     var r = layout.getRegion(key);
31219                     if(r && rstate){
31220                         if(rstate.size){
31221                             r.resizeTo(rstate.size);
31222                         }
31223                         if(rstate.collapsed == true){
31224                             r.collapse(true);
31225                         }else{
31226                             r.expand(null, true);
31227                         }
31228                     }
31229                 }
31230             }
31231             if(!wasUpdating){
31232                 layout.endUpdate();
31233             }
31234             this.state = state; 
31235         }
31236         this.layout = layout;
31237         layout.on("regionresized", this.onRegionResized, this);
31238         layout.on("regioncollapsed", this.onRegionCollapsed, this);
31239         layout.on("regionexpanded", this.onRegionExpanded, this);
31240     },
31241     
31242     storeState : function(){
31243         this.provider.set(this.layout.id+"-layout-state", this.state);
31244     },
31245     
31246     onRegionResized : function(region, newSize){
31247         this.state[region.getPosition()].size = newSize;
31248         this.storeState();
31249     },
31250     
31251     onRegionCollapsed : function(region){
31252         this.state[region.getPosition()].collapsed = true;
31253         this.storeState();
31254     },
31255     
31256     onRegionExpanded : function(region){
31257         this.state[region.getPosition()].collapsed = false;
31258         this.storeState();
31259     }
31260 };/*
31261  * Based on:
31262  * Ext JS Library 1.1.1
31263  * Copyright(c) 2006-2007, Ext JS, LLC.
31264  *
31265  * Originally Released Under LGPL - original licence link has changed is not relivant.
31266  *
31267  * Fork - LGPL
31268  * <script type="text/javascript">
31269  */
31270 /**
31271  * @class Roo.ContentPanel
31272  * @extends Roo.util.Observable
31273  * @children Roo.form.Form Roo.JsonView Roo.View
31274  * @builder-top
31275  * A basic ContentPanel element.
31276  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
31277  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
31278  * @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
31279  * @cfg {Boolean}   closable      True if the panel can be closed/removed
31280  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
31281  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
31282  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
31283  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
31284  * @cfg {String} title          The title for this panel
31285  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
31286  * @cfg {String} url            Calls {@link #setUrl} with this value
31287  * @cfg {String} region [required]   (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
31288  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
31289  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
31290  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
31291  * @cfg {String}    style  Extra style to add to the content panel
31292  * @cfg {Roo.menu.Menu} menu  popup menu
31293
31294  * @constructor
31295  * Create a new ContentPanel.
31296  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
31297  * @param {String/Object} config A string to set only the title or a config object
31298  * @param {String} content (optional) Set the HTML content for this panel
31299  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
31300  */
31301 Roo.ContentPanel = function(el, config, content){
31302     
31303      
31304     /*
31305     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
31306         config = el;
31307         el = Roo.id();
31308     }
31309     if (config && config.parentLayout) { 
31310         el = config.parentLayout.el.createChild(); 
31311     }
31312     */
31313     if(el.autoCreate){ // xtype is available if this is called from factory
31314         config = el;
31315         el = Roo.id();
31316     }
31317     this.el = Roo.get(el);
31318     if(!this.el && config && config.autoCreate){
31319         if(typeof config.autoCreate == "object"){
31320             if(!config.autoCreate.id){
31321                 config.autoCreate.id = config.id||el;
31322             }
31323             this.el = Roo.DomHelper.append(document.body,
31324                         config.autoCreate, true);
31325         }else{
31326             this.el = Roo.DomHelper.append(document.body,
31327                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
31328         }
31329     }
31330     
31331     
31332     this.closable = false;
31333     this.loaded = false;
31334     this.active = false;
31335     if(typeof config == "string"){
31336         this.title = config;
31337     }else{
31338         Roo.apply(this, config);
31339     }
31340     
31341     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31342         this.wrapEl = this.el.wrap();
31343         this.toolbar.container = this.el.insertSibling(false, 'before');
31344         this.toolbar = new Roo.Toolbar(this.toolbar);
31345     }
31346     
31347     // xtype created footer. - not sure if will work as we normally have to render first..
31348     if (this.footer && !this.footer.el && this.footer.xtype) {
31349         if (!this.wrapEl) {
31350             this.wrapEl = this.el.wrap();
31351         }
31352     
31353         this.footer.container = this.wrapEl.createChild();
31354          
31355         this.footer = Roo.factory(this.footer, Roo);
31356         
31357     }
31358     
31359     if(this.resizeEl){
31360         this.resizeEl = Roo.get(this.resizeEl, true);
31361     }else{
31362         this.resizeEl = this.el;
31363     }
31364     // handle view.xtype
31365     
31366  
31367     
31368     
31369     this.addEvents({
31370         /**
31371          * @event activate
31372          * Fires when this panel is activated. 
31373          * @param {Roo.ContentPanel} this
31374          */
31375         "activate" : true,
31376         /**
31377          * @event deactivate
31378          * Fires when this panel is activated. 
31379          * @param {Roo.ContentPanel} this
31380          */
31381         "deactivate" : true,
31382
31383         /**
31384          * @event resize
31385          * Fires when this panel is resized if fitToFrame is true.
31386          * @param {Roo.ContentPanel} this
31387          * @param {Number} width The width after any component adjustments
31388          * @param {Number} height The height after any component adjustments
31389          */
31390         "resize" : true,
31391         
31392          /**
31393          * @event render
31394          * Fires when this tab is created
31395          * @param {Roo.ContentPanel} this
31396          */
31397         "render" : true
31398          
31399         
31400     });
31401     
31402
31403     
31404     
31405     if(this.autoScroll){
31406         this.resizeEl.setStyle("overflow", "auto");
31407     } else {
31408         // fix randome scrolling
31409         this.el.on('scroll', function() {
31410             Roo.log('fix random scolling');
31411             this.scrollTo('top',0); 
31412         });
31413     }
31414     content = content || this.content;
31415     if(content){
31416         this.setContent(content);
31417     }
31418     if(config && config.url){
31419         this.setUrl(this.url, this.params, this.loadOnce);
31420     }
31421     
31422     
31423     
31424     Roo.ContentPanel.superclass.constructor.call(this);
31425     
31426     if (this.view && typeof(this.view.xtype) != 'undefined') {
31427         this.view.el = this.el.appendChild(document.createElement("div"));
31428         this.view = Roo.factory(this.view); 
31429         this.view.render  &&  this.view.render(false, '');  
31430     }
31431     
31432     
31433     this.fireEvent('render', this);
31434 };
31435
31436 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31437     tabTip:'',
31438     setRegion : function(region){
31439         this.region = region;
31440         if(region){
31441            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31442         }else{
31443            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31444         } 
31445     },
31446     
31447     /**
31448      * Returns the toolbar for this Panel if one was configured. 
31449      * @return {Roo.Toolbar} 
31450      */
31451     getToolbar : function(){
31452         return this.toolbar;
31453     },
31454     
31455     setActiveState : function(active){
31456         this.active = active;
31457         if(!active){
31458             this.fireEvent("deactivate", this);
31459         }else{
31460             this.fireEvent("activate", this);
31461         }
31462     },
31463     /**
31464      * Updates this panel's element
31465      * @param {String} content The new content
31466      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31467     */
31468     setContent : function(content, loadScripts){
31469         this.el.update(content, loadScripts);
31470     },
31471
31472     ignoreResize : function(w, h){
31473         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31474             return true;
31475         }else{
31476             this.lastSize = {width: w, height: h};
31477             return false;
31478         }
31479     },
31480     /**
31481      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31482      * @return {Roo.UpdateManager} The UpdateManager
31483      */
31484     getUpdateManager : function(){
31485         return this.el.getUpdateManager();
31486     },
31487      /**
31488      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31489      * @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:
31490 <pre><code>
31491 panel.load({
31492     url: "your-url.php",
31493     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31494     callback: yourFunction,
31495     scope: yourObject, //(optional scope)
31496     discardUrl: false,
31497     nocache: false,
31498     text: "Loading...",
31499     timeout: 30,
31500     scripts: false
31501 });
31502 </code></pre>
31503      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31504      * 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.
31505      * @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}
31506      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31507      * @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.
31508      * @return {Roo.ContentPanel} this
31509      */
31510     load : function(){
31511         var um = this.el.getUpdateManager();
31512         um.update.apply(um, arguments);
31513         return this;
31514     },
31515
31516
31517     /**
31518      * 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.
31519      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31520      * @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)
31521      * @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)
31522      * @return {Roo.UpdateManager} The UpdateManager
31523      */
31524     setUrl : function(url, params, loadOnce){
31525         if(this.refreshDelegate){
31526             this.removeListener("activate", this.refreshDelegate);
31527         }
31528         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31529         this.on("activate", this.refreshDelegate);
31530         return this.el.getUpdateManager();
31531     },
31532     
31533     _handleRefresh : function(url, params, loadOnce){
31534         if(!loadOnce || !this.loaded){
31535             var updater = this.el.getUpdateManager();
31536             updater.update(url, params, this._setLoaded.createDelegate(this));
31537         }
31538     },
31539     
31540     _setLoaded : function(){
31541         this.loaded = true;
31542     }, 
31543     
31544     /**
31545      * Returns this panel's id
31546      * @return {String} 
31547      */
31548     getId : function(){
31549         return this.el.id;
31550     },
31551     
31552     /** 
31553      * Returns this panel's element - used by regiosn to add.
31554      * @return {Roo.Element} 
31555      */
31556     getEl : function(){
31557         return this.wrapEl || this.el;
31558     },
31559     
31560     adjustForComponents : function(width, height)
31561     {
31562         //Roo.log('adjustForComponents ');
31563         if(this.resizeEl != this.el){
31564             width -= this.el.getFrameWidth('lr');
31565             height -= this.el.getFrameWidth('tb');
31566         }
31567         if(this.toolbar){
31568             var te = this.toolbar.getEl();
31569             height -= te.getHeight();
31570             te.setWidth(width);
31571         }
31572         if(this.footer){
31573             var te = this.footer.getEl();
31574             //Roo.log("footer:" + te.getHeight());
31575             
31576             height -= te.getHeight();
31577             te.setWidth(width);
31578         }
31579         
31580         
31581         if(this.adjustments){
31582             width += this.adjustments[0];
31583             height += this.adjustments[1];
31584         }
31585         return {"width": width, "height": height};
31586     },
31587     
31588     setSize : function(width, height){
31589         if(this.fitToFrame && !this.ignoreResize(width, height)){
31590             if(this.fitContainer && this.resizeEl != this.el){
31591                 this.el.setSize(width, height);
31592             }
31593             var size = this.adjustForComponents(width, height);
31594             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31595             this.fireEvent('resize', this, size.width, size.height);
31596         }
31597     },
31598     
31599     /**
31600      * Returns this panel's title
31601      * @return {String} 
31602      */
31603     getTitle : function(){
31604         return this.title;
31605     },
31606     
31607     /**
31608      * Set this panel's title
31609      * @param {String} title
31610      */
31611     setTitle : function(title){
31612         this.title = title;
31613         if(this.region){
31614             this.region.updatePanelTitle(this, title);
31615         }
31616     },
31617     
31618     /**
31619      * Returns true is this panel was configured to be closable
31620      * @return {Boolean} 
31621      */
31622     isClosable : function(){
31623         return this.closable;
31624     },
31625     
31626     beforeSlide : function(){
31627         this.el.clip();
31628         this.resizeEl.clip();
31629     },
31630     
31631     afterSlide : function(){
31632         this.el.unclip();
31633         this.resizeEl.unclip();
31634     },
31635     
31636     /**
31637      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31638      *   Will fail silently if the {@link #setUrl} method has not been called.
31639      *   This does not activate the panel, just updates its content.
31640      */
31641     refresh : function(){
31642         if(this.refreshDelegate){
31643            this.loaded = false;
31644            this.refreshDelegate();
31645         }
31646     },
31647     
31648     /**
31649      * Destroys this panel
31650      */
31651     destroy : function(){
31652         this.el.removeAllListeners();
31653         var tempEl = document.createElement("span");
31654         tempEl.appendChild(this.el.dom);
31655         tempEl.innerHTML = "";
31656         this.el.remove();
31657         this.el = null;
31658     },
31659     
31660     /**
31661      * form - if the content panel contains a form - this is a reference to it.
31662      * @type {Roo.form.Form}
31663      */
31664     form : false,
31665     /**
31666      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31667      *    This contains a reference to it.
31668      * @type {Roo.View}
31669      */
31670     view : false,
31671     
31672       /**
31673      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31674      * <pre><code>
31675
31676 layout.addxtype({
31677        xtype : 'Form',
31678        items: [ .... ]
31679    }
31680 );
31681
31682 </code></pre>
31683      * @param {Object} cfg Xtype definition of item to add.
31684      */
31685     
31686     addxtype : function(cfg) {
31687         // add form..
31688         if (cfg.xtype.match(/^Form$/)) {
31689             
31690             var el;
31691             //if (this.footer) {
31692             //    el = this.footer.container.insertSibling(false, 'before');
31693             //} else {
31694                 el = this.el.createChild();
31695             //}
31696
31697             this.form = new  Roo.form.Form(cfg);
31698             
31699             
31700             if ( this.form.allItems.length) {
31701                 this.form.render(el.dom);
31702             }
31703             return this.form;
31704         }
31705         // should only have one of theses..
31706         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31707             // views.. should not be just added - used named prop 'view''
31708             
31709             cfg.el = this.el.appendChild(document.createElement("div"));
31710             // factory?
31711             
31712             var ret = new Roo.factory(cfg);
31713              
31714              ret.render && ret.render(false, ''); // render blank..
31715             this.view = ret;
31716             return ret;
31717         }
31718         return false;
31719     }
31720 });
31721
31722 /**
31723  * @class Roo.GridPanel
31724  * @extends Roo.ContentPanel
31725  * @constructor
31726  * Create a new GridPanel.
31727  * @param {Roo.grid.Grid} grid The grid for this panel
31728  * @param {String/Object} config A string to set only the panel's title, or a config object
31729  */
31730 Roo.GridPanel = function(grid, config){
31731     
31732   
31733     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31734         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31735         
31736     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31737     
31738     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31739     
31740     if(this.toolbar){
31741         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31742     }
31743     // xtype created footer. - not sure if will work as we normally have to render first..
31744     if (this.footer && !this.footer.el && this.footer.xtype) {
31745         
31746         this.footer.container = this.grid.getView().getFooterPanel(true);
31747         this.footer.dataSource = this.grid.dataSource;
31748         this.footer = Roo.factory(this.footer, Roo);
31749         
31750     }
31751     
31752     grid.monitorWindowResize = false; // turn off autosizing
31753     grid.autoHeight = false;
31754     grid.autoWidth = false;
31755     this.grid = grid;
31756     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31757 };
31758
31759 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31760     getId : function(){
31761         return this.grid.id;
31762     },
31763     
31764     /**
31765      * Returns the grid for this panel
31766      * @return {Roo.grid.Grid} 
31767      */
31768     getGrid : function(){
31769         return this.grid;    
31770     },
31771     
31772     setSize : function(width, height){
31773         if(!this.ignoreResize(width, height)){
31774             var grid = this.grid;
31775             var size = this.adjustForComponents(width, height);
31776             grid.getGridEl().setSize(size.width, size.height);
31777             grid.autoSize();
31778         }
31779     },
31780     
31781     beforeSlide : function(){
31782         this.grid.getView().scroller.clip();
31783     },
31784     
31785     afterSlide : function(){
31786         this.grid.getView().scroller.unclip();
31787     },
31788     
31789     destroy : function(){
31790         this.grid.destroy();
31791         delete this.grid;
31792         Roo.GridPanel.superclass.destroy.call(this); 
31793     }
31794 });
31795
31796
31797 /**
31798  * @class Roo.NestedLayoutPanel
31799  * @extends Roo.ContentPanel
31800  * @constructor
31801  * Create a new NestedLayoutPanel.
31802  * 
31803  * 
31804  * @param {Roo.BorderLayout} layout [required] The layout for this panel
31805  * @param {String/Object} config A string to set only the title or a config object
31806  */
31807 Roo.NestedLayoutPanel = function(layout, config)
31808 {
31809     // construct with only one argument..
31810     /* FIXME - implement nicer consturctors
31811     if (layout.layout) {
31812         config = layout;
31813         layout = config.layout;
31814         delete config.layout;
31815     }
31816     if (layout.xtype && !layout.getEl) {
31817         // then layout needs constructing..
31818         layout = Roo.factory(layout, Roo);
31819     }
31820     */
31821     
31822     
31823     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31824     
31825     layout.monitorWindowResize = false; // turn off autosizing
31826     this.layout = layout;
31827     this.layout.getEl().addClass("x-layout-nested-layout");
31828     
31829     
31830     
31831     
31832 };
31833
31834 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31835
31836     setSize : function(width, height){
31837         if(!this.ignoreResize(width, height)){
31838             var size = this.adjustForComponents(width, height);
31839             var el = this.layout.getEl();
31840             el.setSize(size.width, size.height);
31841             var touch = el.dom.offsetWidth;
31842             this.layout.layout();
31843             // ie requires a double layout on the first pass
31844             if(Roo.isIE && !this.initialized){
31845                 this.initialized = true;
31846                 this.layout.layout();
31847             }
31848         }
31849     },
31850     
31851     // activate all subpanels if not currently active..
31852     
31853     setActiveState : function(active){
31854         this.active = active;
31855         if(!active){
31856             this.fireEvent("deactivate", this);
31857             return;
31858         }
31859         
31860         this.fireEvent("activate", this);
31861         // not sure if this should happen before or after..
31862         if (!this.layout) {
31863             return; // should not happen..
31864         }
31865         var reg = false;
31866         for (var r in this.layout.regions) {
31867             reg = this.layout.getRegion(r);
31868             if (reg.getActivePanel()) {
31869                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31870                 reg.setActivePanel(reg.getActivePanel());
31871                 continue;
31872             }
31873             if (!reg.panels.length) {
31874                 continue;
31875             }
31876             reg.showPanel(reg.getPanel(0));
31877         }
31878         
31879         
31880         
31881         
31882     },
31883     
31884     /**
31885      * Returns the nested BorderLayout for this panel
31886      * @return {Roo.BorderLayout} 
31887      */
31888     getLayout : function(){
31889         return this.layout;
31890     },
31891     
31892      /**
31893      * Adds a xtype elements to the layout of the nested panel
31894      * <pre><code>
31895
31896 panel.addxtype({
31897        xtype : 'ContentPanel',
31898        region: 'west',
31899        items: [ .... ]
31900    }
31901 );
31902
31903 panel.addxtype({
31904         xtype : 'NestedLayoutPanel',
31905         region: 'west',
31906         layout: {
31907            center: { },
31908            west: { }   
31909         },
31910         items : [ ... list of content panels or nested layout panels.. ]
31911    }
31912 );
31913 </code></pre>
31914      * @param {Object} cfg Xtype definition of item to add.
31915      */
31916     addxtype : function(cfg) {
31917         return this.layout.addxtype(cfg);
31918     
31919     }
31920 });
31921
31922 Roo.ScrollPanel = function(el, config, content){
31923     config = config || {};
31924     config.fitToFrame = true;
31925     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31926     
31927     this.el.dom.style.overflow = "hidden";
31928     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31929     this.el.removeClass("x-layout-inactive-content");
31930     this.el.on("mousewheel", this.onWheel, this);
31931
31932     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31933     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31934     up.unselectable(); down.unselectable();
31935     up.on("click", this.scrollUp, this);
31936     down.on("click", this.scrollDown, this);
31937     up.addClassOnOver("x-scroller-btn-over");
31938     down.addClassOnOver("x-scroller-btn-over");
31939     up.addClassOnClick("x-scroller-btn-click");
31940     down.addClassOnClick("x-scroller-btn-click");
31941     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31942
31943     this.resizeEl = this.el;
31944     this.el = wrap; this.up = up; this.down = down;
31945 };
31946
31947 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31948     increment : 100,
31949     wheelIncrement : 5,
31950     scrollUp : function(){
31951         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31952     },
31953
31954     scrollDown : function(){
31955         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31956     },
31957
31958     afterScroll : function(){
31959         var el = this.resizeEl;
31960         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31961         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31962         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31963     },
31964
31965     setSize : function(){
31966         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31967         this.afterScroll();
31968     },
31969
31970     onWheel : function(e){
31971         var d = e.getWheelDelta();
31972         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31973         this.afterScroll();
31974         e.stopEvent();
31975     },
31976
31977     setContent : function(content, loadScripts){
31978         this.resizeEl.update(content, loadScripts);
31979     }
31980
31981 });
31982
31983
31984
31985 /**
31986  * @class Roo.TreePanel
31987  * @extends Roo.ContentPanel
31988  * Treepanel component
31989  * 
31990  * @constructor
31991  * Create a new TreePanel. - defaults to fit/scoll contents.
31992  * @param {String/Object} config A string to set only the panel's title, or a config object
31993  */
31994 Roo.TreePanel = function(config){
31995     var el = config.el;
31996     var tree = config.tree;
31997     delete config.tree; 
31998     delete config.el; // hopefull!
31999     
32000     // wrapper for IE7 strict & safari scroll issue
32001     
32002     var treeEl = el.createChild();
32003     config.resizeEl = treeEl;
32004     
32005     
32006     
32007     Roo.TreePanel.superclass.constructor.call(this, el, config);
32008  
32009  
32010     this.tree = new Roo.tree.TreePanel(treeEl , tree);
32011     //console.log(tree);
32012     this.on('activate', function()
32013     {
32014         if (this.tree.rendered) {
32015             return;
32016         }
32017         //console.log('render tree');
32018         this.tree.render();
32019     });
32020     // this should not be needed.. - it's actually the 'el' that resizes?
32021     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
32022     
32023     //this.on('resize',  function (cp, w, h) {
32024     //        this.tree.innerCt.setWidth(w);
32025     //        this.tree.innerCt.setHeight(h);
32026     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
32027     //});
32028
32029         
32030     
32031 };
32032
32033 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
32034     fitToFrame : true,
32035     autoScroll : true,
32036     /*
32037      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
32038      */
32039     tree : false
32040
32041 });
32042
32043
32044
32045
32046
32047
32048
32049
32050
32051
32052
32053 /*
32054  * Based on:
32055  * Ext JS Library 1.1.1
32056  * Copyright(c) 2006-2007, Ext JS, LLC.
32057  *
32058  * Originally Released Under LGPL - original licence link has changed is not relivant.
32059  *
32060  * Fork - LGPL
32061  * <script type="text/javascript">
32062  */
32063  
32064
32065 /**
32066  * @class Roo.ReaderLayout
32067  * @extends Roo.BorderLayout
32068  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
32069  * center region containing two nested regions (a top one for a list view and one for item preview below),
32070  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
32071  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
32072  * expedites the setup of the overall layout and regions for this common application style.
32073  * Example:
32074  <pre><code>
32075 var reader = new Roo.ReaderLayout();
32076 var CP = Roo.ContentPanel;  // shortcut for adding
32077
32078 reader.beginUpdate();
32079 reader.add("north", new CP("north", "North"));
32080 reader.add("west", new CP("west", {title: "West"}));
32081 reader.add("east", new CP("east", {title: "East"}));
32082
32083 reader.regions.listView.add(new CP("listView", "List"));
32084 reader.regions.preview.add(new CP("preview", "Preview"));
32085 reader.endUpdate();
32086 </code></pre>
32087 * @constructor
32088 * Create a new ReaderLayout
32089 * @param {Object} config Configuration options
32090 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
32091 * document.body if omitted)
32092 */
32093 Roo.ReaderLayout = function(config, renderTo){
32094     var c = config || {size:{}};
32095     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
32096         north: c.north !== false ? Roo.apply({
32097             split:false,
32098             initialSize: 32,
32099             titlebar: false
32100         }, c.north) : false,
32101         west: c.west !== false ? Roo.apply({
32102             split:true,
32103             initialSize: 200,
32104             minSize: 175,
32105             maxSize: 400,
32106             titlebar: true,
32107             collapsible: true,
32108             animate: true,
32109             margins:{left:5,right:0,bottom:5,top:5},
32110             cmargins:{left:5,right:5,bottom:5,top:5}
32111         }, c.west) : false,
32112         east: c.east !== false ? Roo.apply({
32113             split:true,
32114             initialSize: 200,
32115             minSize: 175,
32116             maxSize: 400,
32117             titlebar: true,
32118             collapsible: true,
32119             animate: true,
32120             margins:{left:0,right:5,bottom:5,top:5},
32121             cmargins:{left:5,right:5,bottom:5,top:5}
32122         }, c.east) : false,
32123         center: Roo.apply({
32124             tabPosition: 'top',
32125             autoScroll:false,
32126             closeOnTab: true,
32127             titlebar:false,
32128             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32129         }, c.center)
32130     });
32131
32132     this.el.addClass('x-reader');
32133
32134     this.beginUpdate();
32135
32136     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32137         south: c.preview !== false ? Roo.apply({
32138             split:true,
32139             initialSize: 200,
32140             minSize: 100,
32141             autoScroll:true,
32142             collapsible:true,
32143             titlebar: true,
32144             cmargins:{top:5,left:0, right:0, bottom:0}
32145         }, c.preview) : false,
32146         center: Roo.apply({
32147             autoScroll:false,
32148             titlebar:false,
32149             minHeight:200
32150         }, c.listView)
32151     });
32152     this.add('center', new Roo.NestedLayoutPanel(inner,
32153             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
32154
32155     this.endUpdate();
32156
32157     this.regions.preview = inner.getRegion('south');
32158     this.regions.listView = inner.getRegion('center');
32159 };
32160
32161 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32162  * Based on:
32163  * Ext JS Library 1.1.1
32164  * Copyright(c) 2006-2007, Ext JS, LLC.
32165  *
32166  * Originally Released Under LGPL - original licence link has changed is not relivant.
32167  *
32168  * Fork - LGPL
32169  * <script type="text/javascript">
32170  */
32171  
32172 /**
32173  * @class Roo.grid.Grid
32174  * @extends Roo.util.Observable
32175  * This class represents the primary interface of a component based grid control.
32176  * <br><br>Usage:<pre><code>
32177  var grid = new Roo.grid.Grid("my-container-id", {
32178      ds: myDataStore,
32179      cm: myColModel,
32180      selModel: mySelectionModel,
32181      autoSizeColumns: true,
32182      monitorWindowResize: false,
32183      trackMouseOver: true
32184  });
32185  // set any options
32186  grid.render();
32187  * </code></pre>
32188  * <b>Common Problems:</b><br/>
32189  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32190  * element will correct this<br/>
32191  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32192  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32193  * are unpredictable.<br/>
32194  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32195  * grid to calculate dimensions/offsets.<br/>
32196   * @constructor
32197  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32198  * The container MUST have some type of size defined for the grid to fill. The container will be
32199  * automatically set to position relative if it isn't already.
32200  * @param {Object} config A config object that sets properties on this grid.
32201  */
32202 Roo.grid.Grid = function(container, config){
32203         // initialize the container
32204         this.container = Roo.get(container);
32205         this.container.update("");
32206         this.container.setStyle("overflow", "hidden");
32207     this.container.addClass('x-grid-container');
32208
32209     this.id = this.container.id;
32210
32211     Roo.apply(this, config);
32212     // check and correct shorthanded configs
32213     if(this.ds){
32214         this.dataSource = this.ds;
32215         delete this.ds;
32216     }
32217     if(this.cm){
32218         this.colModel = this.cm;
32219         delete this.cm;
32220     }
32221     if(this.sm){
32222         this.selModel = this.sm;
32223         delete this.sm;
32224     }
32225
32226     if (this.selModel) {
32227         this.selModel = Roo.factory(this.selModel, Roo.grid);
32228         this.sm = this.selModel;
32229         this.sm.xmodule = this.xmodule || false;
32230     }
32231     if (typeof(this.colModel.config) == 'undefined') {
32232         this.colModel = new Roo.grid.ColumnModel(this.colModel);
32233         this.cm = this.colModel;
32234         this.cm.xmodule = this.xmodule || false;
32235     }
32236     if (this.dataSource) {
32237         this.dataSource= Roo.factory(this.dataSource, Roo.data);
32238         this.ds = this.dataSource;
32239         this.ds.xmodule = this.xmodule || false;
32240          
32241     }
32242     
32243     
32244     
32245     if(this.width){
32246         this.container.setWidth(this.width);
32247     }
32248
32249     if(this.height){
32250         this.container.setHeight(this.height);
32251     }
32252     /** @private */
32253         this.addEvents({
32254         // raw events
32255         /**
32256          * @event click
32257          * The raw click event for the entire grid.
32258          * @param {Roo.EventObject} e
32259          */
32260         "click" : true,
32261         /**
32262          * @event dblclick
32263          * The raw dblclick event for the entire grid.
32264          * @param {Roo.EventObject} e
32265          */
32266         "dblclick" : true,
32267         /**
32268          * @event contextmenu
32269          * The raw contextmenu event for the entire grid.
32270          * @param {Roo.EventObject} e
32271          */
32272         "contextmenu" : true,
32273         /**
32274          * @event mousedown
32275          * The raw mousedown event for the entire grid.
32276          * @param {Roo.EventObject} e
32277          */
32278         "mousedown" : true,
32279         /**
32280          * @event mouseup
32281          * The raw mouseup event for the entire grid.
32282          * @param {Roo.EventObject} e
32283          */
32284         "mouseup" : true,
32285         /**
32286          * @event mouseover
32287          * The raw mouseover event for the entire grid.
32288          * @param {Roo.EventObject} e
32289          */
32290         "mouseover" : true,
32291         /**
32292          * @event mouseout
32293          * The raw mouseout event for the entire grid.
32294          * @param {Roo.EventObject} e
32295          */
32296         "mouseout" : true,
32297         /**
32298          * @event keypress
32299          * The raw keypress event for the entire grid.
32300          * @param {Roo.EventObject} e
32301          */
32302         "keypress" : true,
32303         /**
32304          * @event keydown
32305          * The raw keydown event for the entire grid.
32306          * @param {Roo.EventObject} e
32307          */
32308         "keydown" : true,
32309
32310         // custom events
32311
32312         /**
32313          * @event cellclick
32314          * Fires when a cell is clicked
32315          * @param {Grid} this
32316          * @param {Number} rowIndex
32317          * @param {Number} columnIndex
32318          * @param {Roo.EventObject} e
32319          */
32320         "cellclick" : true,
32321         /**
32322          * @event celldblclick
32323          * Fires when a cell is double clicked
32324          * @param {Grid} this
32325          * @param {Number} rowIndex
32326          * @param {Number} columnIndex
32327          * @param {Roo.EventObject} e
32328          */
32329         "celldblclick" : true,
32330         /**
32331          * @event rowclick
32332          * Fires when a row is clicked
32333          * @param {Grid} this
32334          * @param {Number} rowIndex
32335          * @param {Roo.EventObject} e
32336          */
32337         "rowclick" : true,
32338         /**
32339          * @event rowdblclick
32340          * Fires when a row is double clicked
32341          * @param {Grid} this
32342          * @param {Number} rowIndex
32343          * @param {Roo.EventObject} e
32344          */
32345         "rowdblclick" : true,
32346         /**
32347          * @event headerclick
32348          * Fires when a header is clicked
32349          * @param {Grid} this
32350          * @param {Number} columnIndex
32351          * @param {Roo.EventObject} e
32352          */
32353         "headerclick" : true,
32354         /**
32355          * @event headerdblclick
32356          * Fires when a header cell is double clicked
32357          * @param {Grid} this
32358          * @param {Number} columnIndex
32359          * @param {Roo.EventObject} e
32360          */
32361         "headerdblclick" : true,
32362         /**
32363          * @event rowcontextmenu
32364          * Fires when a row is right clicked
32365          * @param {Grid} this
32366          * @param {Number} rowIndex
32367          * @param {Roo.EventObject} e
32368          */
32369         "rowcontextmenu" : true,
32370         /**
32371          * @event cellcontextmenu
32372          * Fires when a cell is right clicked
32373          * @param {Grid} this
32374          * @param {Number} rowIndex
32375          * @param {Number} cellIndex
32376          * @param {Roo.EventObject} e
32377          */
32378          "cellcontextmenu" : true,
32379         /**
32380          * @event headercontextmenu
32381          * Fires when a header is right clicked
32382          * @param {Grid} this
32383          * @param {Number} columnIndex
32384          * @param {Roo.EventObject} e
32385          */
32386         "headercontextmenu" : true,
32387         /**
32388          * @event bodyscroll
32389          * Fires when the body element is scrolled
32390          * @param {Number} scrollLeft
32391          * @param {Number} scrollTop
32392          */
32393         "bodyscroll" : true,
32394         /**
32395          * @event columnresize
32396          * Fires when the user resizes a column
32397          * @param {Number} columnIndex
32398          * @param {Number} newSize
32399          */
32400         "columnresize" : true,
32401         /**
32402          * @event columnmove
32403          * Fires when the user moves a column
32404          * @param {Number} oldIndex
32405          * @param {Number} newIndex
32406          */
32407         "columnmove" : true,
32408         /**
32409          * @event startdrag
32410          * Fires when row(s) start being dragged
32411          * @param {Grid} this
32412          * @param {Roo.GridDD} dd The drag drop object
32413          * @param {event} e The raw browser event
32414          */
32415         "startdrag" : true,
32416         /**
32417          * @event enddrag
32418          * Fires when a drag operation is complete
32419          * @param {Grid} this
32420          * @param {Roo.GridDD} dd The drag drop object
32421          * @param {event} e The raw browser event
32422          */
32423         "enddrag" : true,
32424         /**
32425          * @event dragdrop
32426          * Fires when dragged row(s) are dropped on a valid DD target
32427          * @param {Grid} this
32428          * @param {Roo.GridDD} dd The drag drop object
32429          * @param {String} targetId The target drag drop object
32430          * @param {event} e The raw browser event
32431          */
32432         "dragdrop" : true,
32433         /**
32434          * @event dragover
32435          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32436          * @param {Grid} this
32437          * @param {Roo.GridDD} dd The drag drop object
32438          * @param {String} targetId The target drag drop object
32439          * @param {event} e The raw browser event
32440          */
32441         "dragover" : true,
32442         /**
32443          * @event dragenter
32444          *  Fires when the dragged row(s) first cross another DD target while being dragged
32445          * @param {Grid} this
32446          * @param {Roo.GridDD} dd The drag drop object
32447          * @param {String} targetId The target drag drop object
32448          * @param {event} e The raw browser event
32449          */
32450         "dragenter" : true,
32451         /**
32452          * @event dragout
32453          * Fires when the dragged row(s) leave another DD target while being dragged
32454          * @param {Grid} this
32455          * @param {Roo.GridDD} dd The drag drop object
32456          * @param {String} targetId The target drag drop object
32457          * @param {event} e The raw browser event
32458          */
32459         "dragout" : true,
32460         /**
32461          * @event rowclass
32462          * Fires when a row is rendered, so you can change add a style to it.
32463          * @param {GridView} gridview   The grid view
32464          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
32465          */
32466         'rowclass' : true,
32467
32468         /**
32469          * @event render
32470          * Fires when the grid is rendered
32471          * @param {Grid} grid
32472          */
32473         'render' : true
32474     });
32475
32476     Roo.grid.Grid.superclass.constructor.call(this);
32477 };
32478 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32479     
32480     /**
32481          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
32482          */
32483         /**
32484          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
32485          */
32486         /**
32487          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
32488          */
32489         /**
32490          * @cfg {Roo.grid.Store} ds The data store for the grid
32491          */
32492         /**
32493          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
32494          */
32495         /**
32496      * @cfg {String} ddGroup - drag drop group.
32497      */
32498       /**
32499      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
32500      */
32501
32502     /**
32503      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32504      */
32505     minColumnWidth : 25,
32506
32507     /**
32508      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32509      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32510      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32511      */
32512     autoSizeColumns : false,
32513
32514     /**
32515      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32516      */
32517     autoSizeHeaders : true,
32518
32519     /**
32520      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32521      */
32522     monitorWindowResize : true,
32523
32524     /**
32525      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32526      * rows measured to get a columns size. Default is 0 (all rows).
32527      */
32528     maxRowsToMeasure : 0,
32529
32530     /**
32531      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32532      */
32533     trackMouseOver : true,
32534
32535     /**
32536     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32537     */
32538       /**
32539     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
32540     */
32541     
32542     /**
32543     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32544     */
32545     enableDragDrop : false,
32546     
32547     /**
32548     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32549     */
32550     enableColumnMove : true,
32551     
32552     /**
32553     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32554     */
32555     enableColumnHide : true,
32556     
32557     /**
32558     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32559     */
32560     enableRowHeightSync : false,
32561     
32562     /**
32563     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32564     */
32565     stripeRows : true,
32566     
32567     /**
32568     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32569     */
32570     autoHeight : false,
32571
32572     /**
32573      * @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.
32574      */
32575     autoExpandColumn : false,
32576
32577     /**
32578     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32579     * Default is 50.
32580     */
32581     autoExpandMin : 50,
32582
32583     /**
32584     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32585     */
32586     autoExpandMax : 1000,
32587
32588     /**
32589     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32590     */
32591     view : null,
32592
32593     /**
32594     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32595     */
32596     loadMask : false,
32597     /**
32598     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32599     */
32600     dropTarget: false,
32601     
32602    
32603     
32604     // private
32605     rendered : false,
32606
32607     /**
32608     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32609     * of a fixed width. Default is false.
32610     */
32611     /**
32612     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32613     */
32614     
32615     
32616     /**
32617     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32618     * %0 is replaced with the number of selected rows.
32619     */
32620     ddText : "{0} selected row{1}",
32621     
32622     
32623     /**
32624      * Called once after all setup has been completed and the grid is ready to be rendered.
32625      * @return {Roo.grid.Grid} this
32626      */
32627     render : function()
32628     {
32629         var c = this.container;
32630         // try to detect autoHeight/width mode
32631         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32632             this.autoHeight = true;
32633         }
32634         var view = this.getView();
32635         view.init(this);
32636
32637         c.on("click", this.onClick, this);
32638         c.on("dblclick", this.onDblClick, this);
32639         c.on("contextmenu", this.onContextMenu, this);
32640         c.on("keydown", this.onKeyDown, this);
32641         if (Roo.isTouch) {
32642             c.on("touchstart", this.onTouchStart, this);
32643         }
32644
32645         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32646
32647         this.getSelectionModel().init(this);
32648
32649         view.render();
32650
32651         if(this.loadMask){
32652             this.loadMask = new Roo.LoadMask(this.container,
32653                     Roo.apply({store:this.dataSource}, this.loadMask));
32654         }
32655         
32656         
32657         if (this.toolbar && this.toolbar.xtype) {
32658             this.toolbar.container = this.getView().getHeaderPanel(true);
32659             this.toolbar = new Roo.Toolbar(this.toolbar);
32660         }
32661         if (this.footer && this.footer.xtype) {
32662             this.footer.dataSource = this.getDataSource();
32663             this.footer.container = this.getView().getFooterPanel(true);
32664             this.footer = Roo.factory(this.footer, Roo);
32665         }
32666         if (this.dropTarget && this.dropTarget.xtype) {
32667             delete this.dropTarget.xtype;
32668             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32669         }
32670         
32671         
32672         this.rendered = true;
32673         this.fireEvent('render', this);
32674         return this;
32675     },
32676
32677     /**
32678      * Reconfigures the grid to use a different Store and Column Model.
32679      * The View will be bound to the new objects and refreshed.
32680      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32681      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32682      */
32683     reconfigure : function(dataSource, colModel){
32684         if(this.loadMask){
32685             this.loadMask.destroy();
32686             this.loadMask = new Roo.LoadMask(this.container,
32687                     Roo.apply({store:dataSource}, this.loadMask));
32688         }
32689         this.view.bind(dataSource, colModel);
32690         this.dataSource = dataSource;
32691         this.colModel = colModel;
32692         this.view.refresh(true);
32693     },
32694     /**
32695      * addColumns
32696      * Add's a column, default at the end..
32697      
32698      * @param {int} position to add (default end)
32699      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
32700      */
32701     addColumns : function(pos, ar)
32702     {
32703         
32704         for (var i =0;i< ar.length;i++) {
32705             var cfg = ar[i];
32706             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
32707             this.cm.lookup[cfg.id] = cfg;
32708         }
32709         
32710         
32711         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
32712             pos = this.cm.config.length; //this.cm.config.push(cfg);
32713         } 
32714         pos = Math.max(0,pos);
32715         ar.unshift(0);
32716         ar.unshift(pos);
32717         this.cm.config.splice.apply(this.cm.config, ar);
32718         
32719         
32720         
32721         this.view.generateRules(this.cm);
32722         this.view.refresh(true);
32723         
32724     },
32725     
32726     
32727     
32728     
32729     // private
32730     onKeyDown : function(e){
32731         this.fireEvent("keydown", e);
32732     },
32733
32734     /**
32735      * Destroy this grid.
32736      * @param {Boolean} removeEl True to remove the element
32737      */
32738     destroy : function(removeEl, keepListeners){
32739         if(this.loadMask){
32740             this.loadMask.destroy();
32741         }
32742         var c = this.container;
32743         c.removeAllListeners();
32744         this.view.destroy();
32745         this.colModel.purgeListeners();
32746         if(!keepListeners){
32747             this.purgeListeners();
32748         }
32749         c.update("");
32750         if(removeEl === true){
32751             c.remove();
32752         }
32753     },
32754
32755     // private
32756     processEvent : function(name, e){
32757         // does this fire select???
32758         //Roo.log('grid:processEvent '  + name);
32759         
32760         if (name != 'touchstart' ) {
32761             this.fireEvent(name, e);    
32762         }
32763         
32764         var t = e.getTarget();
32765         var v = this.view;
32766         var header = v.findHeaderIndex(t);
32767         if(header !== false){
32768             var ename = name == 'touchstart' ? 'click' : name;
32769              
32770             this.fireEvent("header" + ename, this, header, e);
32771         }else{
32772             var row = v.findRowIndex(t);
32773             var cell = v.findCellIndex(t);
32774             if (name == 'touchstart') {
32775                 // first touch is always a click.
32776                 // hopefull this happens after selection is updated.?
32777                 name = false;
32778                 
32779                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32780                     var cs = this.selModel.getSelectedCell();
32781                     if (row == cs[0] && cell == cs[1]){
32782                         name = 'dblclick';
32783                     }
32784                 }
32785                 if (typeof(this.selModel.getSelections) != 'undefined') {
32786                     var cs = this.selModel.getSelections();
32787                     var ds = this.dataSource;
32788                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
32789                         name = 'dblclick';
32790                     }
32791                 }
32792                 if (!name) {
32793                     return;
32794                 }
32795             }
32796             
32797             
32798             if(row !== false){
32799                 this.fireEvent("row" + name, this, row, e);
32800                 if(cell !== false){
32801                     this.fireEvent("cell" + name, this, row, cell, e);
32802                 }
32803             }
32804         }
32805     },
32806
32807     // private
32808     onClick : function(e){
32809         this.processEvent("click", e);
32810     },
32811    // private
32812     onTouchStart : function(e){
32813         this.processEvent("touchstart", e);
32814     },
32815
32816     // private
32817     onContextMenu : function(e, t){
32818         this.processEvent("contextmenu", e);
32819     },
32820
32821     // private
32822     onDblClick : function(e){
32823         this.processEvent("dblclick", e);
32824     },
32825
32826     // private
32827     walkCells : function(row, col, step, fn, scope){
32828         var cm = this.colModel, clen = cm.getColumnCount();
32829         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32830         if(step < 0){
32831             if(col < 0){
32832                 row--;
32833                 first = false;
32834             }
32835             while(row >= 0){
32836                 if(!first){
32837                     col = clen-1;
32838                 }
32839                 first = false;
32840                 while(col >= 0){
32841                     if(fn.call(scope || this, row, col, cm) === true){
32842                         return [row, col];
32843                     }
32844                     col--;
32845                 }
32846                 row--;
32847             }
32848         } else {
32849             if(col >= clen){
32850                 row++;
32851                 first = false;
32852             }
32853             while(row < rlen){
32854                 if(!first){
32855                     col = 0;
32856                 }
32857                 first = false;
32858                 while(col < clen){
32859                     if(fn.call(scope || this, row, col, cm) === true){
32860                         return [row, col];
32861                     }
32862                     col++;
32863                 }
32864                 row++;
32865             }
32866         }
32867         return null;
32868     },
32869
32870     // private
32871     getSelections : function(){
32872         return this.selModel.getSelections();
32873     },
32874
32875     /**
32876      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32877      * but if manual update is required this method will initiate it.
32878      */
32879     autoSize : function(){
32880         if(this.rendered){
32881             this.view.layout();
32882             if(this.view.adjustForScroll){
32883                 this.view.adjustForScroll();
32884             }
32885         }
32886     },
32887
32888     /**
32889      * Returns the grid's underlying element.
32890      * @return {Element} The element
32891      */
32892     getGridEl : function(){
32893         return this.container;
32894     },
32895
32896     // private for compatibility, overridden by editor grid
32897     stopEditing : function(){},
32898
32899     /**
32900      * Returns the grid's SelectionModel.
32901      * @return {SelectionModel}
32902      */
32903     getSelectionModel : function(){
32904         if(!this.selModel){
32905             this.selModel = new Roo.grid.RowSelectionModel();
32906         }
32907         return this.selModel;
32908     },
32909
32910     /**
32911      * Returns the grid's DataSource.
32912      * @return {DataSource}
32913      */
32914     getDataSource : function(){
32915         return this.dataSource;
32916     },
32917
32918     /**
32919      * Returns the grid's ColumnModel.
32920      * @return {ColumnModel}
32921      */
32922     getColumnModel : function(){
32923         return this.colModel;
32924     },
32925
32926     /**
32927      * Returns the grid's GridView object.
32928      * @return {GridView}
32929      */
32930     getView : function(){
32931         if(!this.view){
32932             this.view = new Roo.grid.GridView(this.viewConfig);
32933             this.relayEvents(this.view, [
32934                 "beforerowremoved", "beforerowsinserted",
32935                 "beforerefresh", "rowremoved",
32936                 "rowsinserted", "rowupdated" ,"refresh"
32937             ]);
32938         }
32939         return this.view;
32940     },
32941     /**
32942      * Called to get grid's drag proxy text, by default returns this.ddText.
32943      * Override this to put something different in the dragged text.
32944      * @return {String}
32945      */
32946     getDragDropText : function(){
32947         var count = this.selModel.getCount();
32948         return String.format(this.ddText, count, count == 1 ? '' : 's');
32949     }
32950 });
32951 /*
32952  * Based on:
32953  * Ext JS Library 1.1.1
32954  * Copyright(c) 2006-2007, Ext JS, LLC.
32955  *
32956  * Originally Released Under LGPL - original licence link has changed is not relivant.
32957  *
32958  * Fork - LGPL
32959  * <script type="text/javascript">
32960  */
32961  /**
32962  * @class Roo.grid.AbstractGridView
32963  * @extends Roo.util.Observable
32964  * @abstract
32965  * Abstract base class for grid Views
32966  * @constructor
32967  */
32968 Roo.grid.AbstractGridView = function(){
32969         this.grid = null;
32970         
32971         this.events = {
32972             "beforerowremoved" : true,
32973             "beforerowsinserted" : true,
32974             "beforerefresh" : true,
32975             "rowremoved" : true,
32976             "rowsinserted" : true,
32977             "rowupdated" : true,
32978             "refresh" : true
32979         };
32980     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32981 };
32982
32983 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32984     rowClass : "x-grid-row",
32985     cellClass : "x-grid-cell",
32986     tdClass : "x-grid-td",
32987     hdClass : "x-grid-hd",
32988     splitClass : "x-grid-hd-split",
32989     
32990     init: function(grid){
32991         this.grid = grid;
32992                 var cid = this.grid.getGridEl().id;
32993         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32994         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32995         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32996         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32997         },
32998         
32999     getColumnRenderers : function(){
33000         var renderers = [];
33001         var cm = this.grid.colModel;
33002         var colCount = cm.getColumnCount();
33003         for(var i = 0; i < colCount; i++){
33004             renderers[i] = cm.getRenderer(i);
33005         }
33006         return renderers;
33007     },
33008     
33009     getColumnIds : function(){
33010         var ids = [];
33011         var cm = this.grid.colModel;
33012         var colCount = cm.getColumnCount();
33013         for(var i = 0; i < colCount; i++){
33014             ids[i] = cm.getColumnId(i);
33015         }
33016         return ids;
33017     },
33018     
33019     getDataIndexes : function(){
33020         if(!this.indexMap){
33021             this.indexMap = this.buildIndexMap();
33022         }
33023         return this.indexMap.colToData;
33024     },
33025     
33026     getColumnIndexByDataIndex : function(dataIndex){
33027         if(!this.indexMap){
33028             this.indexMap = this.buildIndexMap();
33029         }
33030         return this.indexMap.dataToCol[dataIndex];
33031     },
33032     
33033     /**
33034      * Set a css style for a column dynamically. 
33035      * @param {Number} colIndex The index of the column
33036      * @param {String} name The css property name
33037      * @param {String} value The css value
33038      */
33039     setCSSStyle : function(colIndex, name, value){
33040         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
33041         Roo.util.CSS.updateRule(selector, name, value);
33042     },
33043     
33044     generateRules : function(cm){
33045         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
33046         Roo.util.CSS.removeStyleSheet(rulesId);
33047         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33048             var cid = cm.getColumnId(i);
33049             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
33050                          this.tdSelector, cid, " {\n}\n",
33051                          this.hdSelector, cid, " {\n}\n",
33052                          this.splitSelector, cid, " {\n}\n");
33053         }
33054         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33055     }
33056 });/*
33057  * Based on:
33058  * Ext JS Library 1.1.1
33059  * Copyright(c) 2006-2007, Ext JS, LLC.
33060  *
33061  * Originally Released Under LGPL - original licence link has changed is not relivant.
33062  *
33063  * Fork - LGPL
33064  * <script type="text/javascript">
33065  */
33066
33067 // private
33068 // This is a support class used internally by the Grid components
33069 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
33070     this.grid = grid;
33071     this.view = grid.getView();
33072     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33073     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
33074     if(hd2){
33075         this.setHandleElId(Roo.id(hd));
33076         this.setOuterHandleElId(Roo.id(hd2));
33077     }
33078     this.scroll = false;
33079 };
33080 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
33081     maxDragWidth: 120,
33082     getDragData : function(e){
33083         var t = Roo.lib.Event.getTarget(e);
33084         var h = this.view.findHeaderCell(t);
33085         if(h){
33086             return {ddel: h.firstChild, header:h};
33087         }
33088         return false;
33089     },
33090
33091     onInitDrag : function(e){
33092         this.view.headersDisabled = true;
33093         var clone = this.dragData.ddel.cloneNode(true);
33094         clone.id = Roo.id();
33095         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
33096         this.proxy.update(clone);
33097         return true;
33098     },
33099
33100     afterValidDrop : function(){
33101         var v = this.view;
33102         setTimeout(function(){
33103             v.headersDisabled = false;
33104         }, 50);
33105     },
33106
33107     afterInvalidDrop : function(){
33108         var v = this.view;
33109         setTimeout(function(){
33110             v.headersDisabled = false;
33111         }, 50);
33112     }
33113 });
33114 /*
33115  * Based on:
33116  * Ext JS Library 1.1.1
33117  * Copyright(c) 2006-2007, Ext JS, LLC.
33118  *
33119  * Originally Released Under LGPL - original licence link has changed is not relivant.
33120  *
33121  * Fork - LGPL
33122  * <script type="text/javascript">
33123  */
33124 // private
33125 // This is a support class used internally by the Grid components
33126 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
33127     this.grid = grid;
33128     this.view = grid.getView();
33129     // split the proxies so they don't interfere with mouse events
33130     this.proxyTop = Roo.DomHelper.append(document.body, {
33131         cls:"col-move-top", html:"&#160;"
33132     }, true);
33133     this.proxyBottom = Roo.DomHelper.append(document.body, {
33134         cls:"col-move-bottom", html:"&#160;"
33135     }, true);
33136     this.proxyTop.hide = this.proxyBottom.hide = function(){
33137         this.setLeftTop(-100,-100);
33138         this.setStyle("visibility", "hidden");
33139     };
33140     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33141     // temporarily disabled
33142     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
33143     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
33144 };
33145 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
33146     proxyOffsets : [-4, -9],
33147     fly: Roo.Element.fly,
33148
33149     getTargetFromEvent : function(e){
33150         var t = Roo.lib.Event.getTarget(e);
33151         var cindex = this.view.findCellIndex(t);
33152         if(cindex !== false){
33153             return this.view.getHeaderCell(cindex);
33154         }
33155         return null;
33156     },
33157
33158     nextVisible : function(h){
33159         var v = this.view, cm = this.grid.colModel;
33160         h = h.nextSibling;
33161         while(h){
33162             if(!cm.isHidden(v.getCellIndex(h))){
33163                 return h;
33164             }
33165             h = h.nextSibling;
33166         }
33167         return null;
33168     },
33169
33170     prevVisible : function(h){
33171         var v = this.view, cm = this.grid.colModel;
33172         h = h.prevSibling;
33173         while(h){
33174             if(!cm.isHidden(v.getCellIndex(h))){
33175                 return h;
33176             }
33177             h = h.prevSibling;
33178         }
33179         return null;
33180     },
33181
33182     positionIndicator : function(h, n, e){
33183         var x = Roo.lib.Event.getPageX(e);
33184         var r = Roo.lib.Dom.getRegion(n.firstChild);
33185         var px, pt, py = r.top + this.proxyOffsets[1];
33186         if((r.right - x) <= (r.right-r.left)/2){
33187             px = r.right+this.view.borderWidth;
33188             pt = "after";
33189         }else{
33190             px = r.left;
33191             pt = "before";
33192         }
33193         var oldIndex = this.view.getCellIndex(h);
33194         var newIndex = this.view.getCellIndex(n);
33195
33196         if(this.grid.colModel.isFixed(newIndex)){
33197             return false;
33198         }
33199
33200         var locked = this.grid.colModel.isLocked(newIndex);
33201
33202         if(pt == "after"){
33203             newIndex++;
33204         }
33205         if(oldIndex < newIndex){
33206             newIndex--;
33207         }
33208         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33209             return false;
33210         }
33211         px +=  this.proxyOffsets[0];
33212         this.proxyTop.setLeftTop(px, py);
33213         this.proxyTop.show();
33214         if(!this.bottomOffset){
33215             this.bottomOffset = this.view.mainHd.getHeight();
33216         }
33217         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33218         this.proxyBottom.show();
33219         return pt;
33220     },
33221
33222     onNodeEnter : function(n, dd, e, data){
33223         if(data.header != n){
33224             this.positionIndicator(data.header, n, e);
33225         }
33226     },
33227
33228     onNodeOver : function(n, dd, e, data){
33229         var result = false;
33230         if(data.header != n){
33231             result = this.positionIndicator(data.header, n, e);
33232         }
33233         if(!result){
33234             this.proxyTop.hide();
33235             this.proxyBottom.hide();
33236         }
33237         return result ? this.dropAllowed : this.dropNotAllowed;
33238     },
33239
33240     onNodeOut : function(n, dd, e, data){
33241         this.proxyTop.hide();
33242         this.proxyBottom.hide();
33243     },
33244
33245     onNodeDrop : function(n, dd, e, data){
33246         var h = data.header;
33247         if(h != n){
33248             var cm = this.grid.colModel;
33249             var x = Roo.lib.Event.getPageX(e);
33250             var r = Roo.lib.Dom.getRegion(n.firstChild);
33251             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33252             var oldIndex = this.view.getCellIndex(h);
33253             var newIndex = this.view.getCellIndex(n);
33254             var locked = cm.isLocked(newIndex);
33255             if(pt == "after"){
33256                 newIndex++;
33257             }
33258             if(oldIndex < newIndex){
33259                 newIndex--;
33260             }
33261             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33262                 return false;
33263             }
33264             cm.setLocked(oldIndex, locked, true);
33265             cm.moveColumn(oldIndex, newIndex);
33266             this.grid.fireEvent("columnmove", oldIndex, newIndex);
33267             return true;
33268         }
33269         return false;
33270     }
33271 });
33272 /*
33273  * Based on:
33274  * Ext JS Library 1.1.1
33275  * Copyright(c) 2006-2007, Ext JS, LLC.
33276  *
33277  * Originally Released Under LGPL - original licence link has changed is not relivant.
33278  *
33279  * Fork - LGPL
33280  * <script type="text/javascript">
33281  */
33282   
33283 /**
33284  * @class Roo.grid.GridView
33285  * @extends Roo.util.Observable
33286  *
33287  * @constructor
33288  * @param {Object} config
33289  */
33290 Roo.grid.GridView = function(config){
33291     Roo.grid.GridView.superclass.constructor.call(this);
33292     this.el = null;
33293
33294     Roo.apply(this, config);
33295 };
33296
33297 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33298
33299     unselectable :  'unselectable="on"',
33300     unselectableCls :  'x-unselectable',
33301     
33302     
33303     rowClass : "x-grid-row",
33304
33305     cellClass : "x-grid-col",
33306
33307     tdClass : "x-grid-td",
33308
33309     hdClass : "x-grid-hd",
33310
33311     splitClass : "x-grid-split",
33312
33313     sortClasses : ["sort-asc", "sort-desc"],
33314
33315     enableMoveAnim : false,
33316
33317     hlColor: "C3DAF9",
33318
33319     dh : Roo.DomHelper,
33320
33321     fly : Roo.Element.fly,
33322
33323     css : Roo.util.CSS,
33324
33325     borderWidth: 1,
33326
33327     splitOffset: 3,
33328
33329     scrollIncrement : 22,
33330
33331     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33332
33333     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33334
33335     bind : function(ds, cm){
33336         if(this.ds){
33337             this.ds.un("load", this.onLoad, this);
33338             this.ds.un("datachanged", this.onDataChange, this);
33339             this.ds.un("add", this.onAdd, this);
33340             this.ds.un("remove", this.onRemove, this);
33341             this.ds.un("update", this.onUpdate, this);
33342             this.ds.un("clear", this.onClear, this);
33343         }
33344         if(ds){
33345             ds.on("load", this.onLoad, this);
33346             ds.on("datachanged", this.onDataChange, this);
33347             ds.on("add", this.onAdd, this);
33348             ds.on("remove", this.onRemove, this);
33349             ds.on("update", this.onUpdate, this);
33350             ds.on("clear", this.onClear, this);
33351         }
33352         this.ds = ds;
33353
33354         if(this.cm){
33355             this.cm.un("widthchange", this.onColWidthChange, this);
33356             this.cm.un("headerchange", this.onHeaderChange, this);
33357             this.cm.un("hiddenchange", this.onHiddenChange, this);
33358             this.cm.un("columnmoved", this.onColumnMove, this);
33359             this.cm.un("columnlockchange", this.onColumnLock, this);
33360         }
33361         if(cm){
33362             this.generateRules(cm);
33363             cm.on("widthchange", this.onColWidthChange, this);
33364             cm.on("headerchange", this.onHeaderChange, this);
33365             cm.on("hiddenchange", this.onHiddenChange, this);
33366             cm.on("columnmoved", this.onColumnMove, this);
33367             cm.on("columnlockchange", this.onColumnLock, this);
33368         }
33369         this.cm = cm;
33370     },
33371
33372     init: function(grid){
33373         Roo.grid.GridView.superclass.init.call(this, grid);
33374
33375         this.bind(grid.dataSource, grid.colModel);
33376
33377         grid.on("headerclick", this.handleHeaderClick, this);
33378
33379         if(grid.trackMouseOver){
33380             grid.on("mouseover", this.onRowOver, this);
33381             grid.on("mouseout", this.onRowOut, this);
33382         }
33383         grid.cancelTextSelection = function(){};
33384         this.gridId = grid.id;
33385
33386         var tpls = this.templates || {};
33387
33388         if(!tpls.master){
33389             tpls.master = new Roo.Template(
33390                '<div class="x-grid" hidefocus="true">',
33391                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
33392                   '<div class="x-grid-topbar"></div>',
33393                   '<div class="x-grid-scroller"><div></div></div>',
33394                   '<div class="x-grid-locked">',
33395                       '<div class="x-grid-header">{lockedHeader}</div>',
33396                       '<div class="x-grid-body">{lockedBody}</div>',
33397                   "</div>",
33398                   '<div class="x-grid-viewport">',
33399                       '<div class="x-grid-header">{header}</div>',
33400                       '<div class="x-grid-body">{body}</div>',
33401                   "</div>",
33402                   '<div class="x-grid-bottombar"></div>',
33403                  
33404                   '<div class="x-grid-resize-proxy">&#160;</div>',
33405                "</div>"
33406             );
33407             tpls.master.disableformats = true;
33408         }
33409
33410         if(!tpls.header){
33411             tpls.header = new Roo.Template(
33412                '<table border="0" cellspacing="0" cellpadding="0">',
33413                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
33414                "</table>{splits}"
33415             );
33416             tpls.header.disableformats = true;
33417         }
33418         tpls.header.compile();
33419
33420         if(!tpls.hcell){
33421             tpls.hcell = new Roo.Template(
33422                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
33423                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
33424                 "</div></td>"
33425              );
33426              tpls.hcell.disableFormats = true;
33427         }
33428         tpls.hcell.compile();
33429
33430         if(!tpls.hsplit){
33431             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
33432                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
33433             tpls.hsplit.disableFormats = true;
33434         }
33435         tpls.hsplit.compile();
33436
33437         if(!tpls.body){
33438             tpls.body = new Roo.Template(
33439                '<table border="0" cellspacing="0" cellpadding="0">',
33440                "<tbody>{rows}</tbody>",
33441                "</table>"
33442             );
33443             tpls.body.disableFormats = true;
33444         }
33445         tpls.body.compile();
33446
33447         if(!tpls.row){
33448             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
33449             tpls.row.disableFormats = true;
33450         }
33451         tpls.row.compile();
33452
33453         if(!tpls.cell){
33454             tpls.cell = new Roo.Template(
33455                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
33456                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
33457                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
33458                 "</td>"
33459             );
33460             tpls.cell.disableFormats = true;
33461         }
33462         tpls.cell.compile();
33463
33464         this.templates = tpls;
33465     },
33466
33467     // remap these for backwards compat
33468     onColWidthChange : function(){
33469         this.updateColumns.apply(this, arguments);
33470     },
33471     onHeaderChange : function(){
33472         this.updateHeaders.apply(this, arguments);
33473     }, 
33474     onHiddenChange : function(){
33475         this.handleHiddenChange.apply(this, arguments);
33476     },
33477     onColumnMove : function(){
33478         this.handleColumnMove.apply(this, arguments);
33479     },
33480     onColumnLock : function(){
33481         this.handleLockChange.apply(this, arguments);
33482     },
33483
33484     onDataChange : function(){
33485         this.refresh();
33486         this.updateHeaderSortState();
33487     },
33488
33489     onClear : function(){
33490         this.refresh();
33491     },
33492
33493     onUpdate : function(ds, record){
33494         this.refreshRow(record);
33495     },
33496
33497     refreshRow : function(record){
33498         var ds = this.ds, index;
33499         if(typeof record == 'number'){
33500             index = record;
33501             record = ds.getAt(index);
33502         }else{
33503             index = ds.indexOf(record);
33504         }
33505         this.insertRows(ds, index, index, true);
33506         this.onRemove(ds, record, index+1, true);
33507         this.syncRowHeights(index, index);
33508         this.layout();
33509         this.fireEvent("rowupdated", this, index, record);
33510     },
33511
33512     onAdd : function(ds, records, index){
33513         this.insertRows(ds, index, index + (records.length-1));
33514     },
33515
33516     onRemove : function(ds, record, index, isUpdate){
33517         if(isUpdate !== true){
33518             this.fireEvent("beforerowremoved", this, index, record);
33519         }
33520         var bt = this.getBodyTable(), lt = this.getLockedTable();
33521         if(bt.rows[index]){
33522             bt.firstChild.removeChild(bt.rows[index]);
33523         }
33524         if(lt.rows[index]){
33525             lt.firstChild.removeChild(lt.rows[index]);
33526         }
33527         if(isUpdate !== true){
33528             this.stripeRows(index);
33529             this.syncRowHeights(index, index);
33530             this.layout();
33531             this.fireEvent("rowremoved", this, index, record);
33532         }
33533     },
33534
33535     onLoad : function(){
33536         this.scrollToTop();
33537     },
33538
33539     /**
33540      * Scrolls the grid to the top
33541      */
33542     scrollToTop : function(){
33543         if(this.scroller){
33544             this.scroller.dom.scrollTop = 0;
33545             this.syncScroll();
33546         }
33547     },
33548
33549     /**
33550      * Gets a panel in the header of the grid that can be used for toolbars etc.
33551      * After modifying the contents of this panel a call to grid.autoSize() may be
33552      * required to register any changes in size.
33553      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33554      * @return Roo.Element
33555      */
33556     getHeaderPanel : function(doShow){
33557         if(doShow){
33558             this.headerPanel.show();
33559         }
33560         return this.headerPanel;
33561     },
33562
33563     /**
33564      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33565      * After modifying the contents of this panel a call to grid.autoSize() may be
33566      * required to register any changes in size.
33567      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33568      * @return Roo.Element
33569      */
33570     getFooterPanel : function(doShow){
33571         if(doShow){
33572             this.footerPanel.show();
33573         }
33574         return this.footerPanel;
33575     },
33576
33577     initElements : function(){
33578         var E = Roo.Element;
33579         var el = this.grid.getGridEl().dom.firstChild;
33580         var cs = el.childNodes;
33581
33582         this.el = new E(el);
33583         
33584          this.focusEl = new E(el.firstChild);
33585         this.focusEl.swallowEvent("click", true);
33586         
33587         this.headerPanel = new E(cs[1]);
33588         this.headerPanel.enableDisplayMode("block");
33589
33590         this.scroller = new E(cs[2]);
33591         this.scrollSizer = new E(this.scroller.dom.firstChild);
33592
33593         this.lockedWrap = new E(cs[3]);
33594         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33595         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33596
33597         this.mainWrap = new E(cs[4]);
33598         this.mainHd = new E(this.mainWrap.dom.firstChild);
33599         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33600
33601         this.footerPanel = new E(cs[5]);
33602         this.footerPanel.enableDisplayMode("block");
33603
33604         this.resizeProxy = new E(cs[6]);
33605
33606         this.headerSelector = String.format(
33607            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33608            this.lockedHd.id, this.mainHd.id
33609         );
33610
33611         this.splitterSelector = String.format(
33612            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33613            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33614         );
33615     },
33616     idToCssName : function(s)
33617     {
33618         return s.replace(/[^a-z0-9]+/ig, '-');
33619     },
33620
33621     getHeaderCell : function(index){
33622         return Roo.DomQuery.select(this.headerSelector)[index];
33623     },
33624
33625     getHeaderCellMeasure : function(index){
33626         return this.getHeaderCell(index).firstChild;
33627     },
33628
33629     getHeaderCellText : function(index){
33630         return this.getHeaderCell(index).firstChild.firstChild;
33631     },
33632
33633     getLockedTable : function(){
33634         return this.lockedBody.dom.firstChild;
33635     },
33636
33637     getBodyTable : function(){
33638         return this.mainBody.dom.firstChild;
33639     },
33640
33641     getLockedRow : function(index){
33642         return this.getLockedTable().rows[index];
33643     },
33644
33645     getRow : function(index){
33646         return this.getBodyTable().rows[index];
33647     },
33648
33649     getRowComposite : function(index){
33650         if(!this.rowEl){
33651             this.rowEl = new Roo.CompositeElementLite();
33652         }
33653         var els = [], lrow, mrow;
33654         if(lrow = this.getLockedRow(index)){
33655             els.push(lrow);
33656         }
33657         if(mrow = this.getRow(index)){
33658             els.push(mrow);
33659         }
33660         this.rowEl.elements = els;
33661         return this.rowEl;
33662     },
33663     /**
33664      * Gets the 'td' of the cell
33665      * 
33666      * @param {Integer} rowIndex row to select
33667      * @param {Integer} colIndex column to select
33668      * 
33669      * @return {Object} 
33670      */
33671     getCell : function(rowIndex, colIndex){
33672         var locked = this.cm.getLockedCount();
33673         var source;
33674         if(colIndex < locked){
33675             source = this.lockedBody.dom.firstChild;
33676         }else{
33677             source = this.mainBody.dom.firstChild;
33678             colIndex -= locked;
33679         }
33680         return source.rows[rowIndex].childNodes[colIndex];
33681     },
33682
33683     getCellText : function(rowIndex, colIndex){
33684         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33685     },
33686
33687     getCellBox : function(cell){
33688         var b = this.fly(cell).getBox();
33689         if(Roo.isOpera){ // opera fails to report the Y
33690             b.y = cell.offsetTop + this.mainBody.getY();
33691         }
33692         return b;
33693     },
33694
33695     getCellIndex : function(cell){
33696         var id = String(cell.className).match(this.cellRE);
33697         if(id){
33698             return parseInt(id[1], 10);
33699         }
33700         return 0;
33701     },
33702
33703     findHeaderIndex : function(n){
33704         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33705         return r ? this.getCellIndex(r) : false;
33706     },
33707
33708     findHeaderCell : function(n){
33709         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33710         return r ? r : false;
33711     },
33712
33713     findRowIndex : function(n){
33714         if(!n){
33715             return false;
33716         }
33717         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33718         return r ? r.rowIndex : false;
33719     },
33720
33721     findCellIndex : function(node){
33722         var stop = this.el.dom;
33723         while(node && node != stop){
33724             if(this.findRE.test(node.className)){
33725                 return this.getCellIndex(node);
33726             }
33727             node = node.parentNode;
33728         }
33729         return false;
33730     },
33731
33732     getColumnId : function(index){
33733         return this.cm.getColumnId(index);
33734     },
33735
33736     getSplitters : function()
33737     {
33738         if(this.splitterSelector){
33739            return Roo.DomQuery.select(this.splitterSelector);
33740         }else{
33741             return null;
33742       }
33743     },
33744
33745     getSplitter : function(index){
33746         return this.getSplitters()[index];
33747     },
33748
33749     onRowOver : function(e, t){
33750         var row;
33751         if((row = this.findRowIndex(t)) !== false){
33752             this.getRowComposite(row).addClass("x-grid-row-over");
33753         }
33754     },
33755
33756     onRowOut : function(e, t){
33757         var row;
33758         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33759             this.getRowComposite(row).removeClass("x-grid-row-over");
33760         }
33761     },
33762
33763     renderHeaders : function(){
33764         var cm = this.cm;
33765         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33766         var cb = [], lb = [], sb = [], lsb = [], p = {};
33767         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33768             p.cellId = "x-grid-hd-0-" + i;
33769             p.splitId = "x-grid-csplit-0-" + i;
33770             p.id = cm.getColumnId(i);
33771             p.value = cm.getColumnHeader(i) || "";
33772             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
33773             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33774             if(!cm.isLocked(i)){
33775                 cb[cb.length] = ct.apply(p);
33776                 sb[sb.length] = st.apply(p);
33777             }else{
33778                 lb[lb.length] = ct.apply(p);
33779                 lsb[lsb.length] = st.apply(p);
33780             }
33781         }
33782         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33783                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33784     },
33785
33786     updateHeaders : function(){
33787         var html = this.renderHeaders();
33788         this.lockedHd.update(html[0]);
33789         this.mainHd.update(html[1]);
33790     },
33791
33792     /**
33793      * Focuses the specified row.
33794      * @param {Number} row The row index
33795      */
33796     focusRow : function(row)
33797     {
33798         //Roo.log('GridView.focusRow');
33799         var x = this.scroller.dom.scrollLeft;
33800         this.focusCell(row, 0, false);
33801         this.scroller.dom.scrollLeft = x;
33802     },
33803
33804     /**
33805      * Focuses the specified cell.
33806      * @param {Number} row The row index
33807      * @param {Number} col The column index
33808      * @param {Boolean} hscroll false to disable horizontal scrolling
33809      */
33810     focusCell : function(row, col, hscroll)
33811     {
33812         //Roo.log('GridView.focusCell');
33813         var el = this.ensureVisible(row, col, hscroll);
33814         this.focusEl.alignTo(el, "tl-tl");
33815         if(Roo.isGecko){
33816             this.focusEl.focus();
33817         }else{
33818             this.focusEl.focus.defer(1, this.focusEl);
33819         }
33820     },
33821
33822     /**
33823      * Scrolls the specified cell into view
33824      * @param {Number} row The row index
33825      * @param {Number} col The column index
33826      * @param {Boolean} hscroll false to disable horizontal scrolling
33827      */
33828     ensureVisible : function(row, col, hscroll)
33829     {
33830         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33831         //return null; //disable for testing.
33832         if(typeof row != "number"){
33833             row = row.rowIndex;
33834         }
33835         if(row < 0 && row >= this.ds.getCount()){
33836             return  null;
33837         }
33838         col = (col !== undefined ? col : 0);
33839         var cm = this.grid.colModel;
33840         while(cm.isHidden(col)){
33841             col++;
33842         }
33843
33844         var el = this.getCell(row, col);
33845         if(!el){
33846             return null;
33847         }
33848         var c = this.scroller.dom;
33849
33850         var ctop = parseInt(el.offsetTop, 10);
33851         var cleft = parseInt(el.offsetLeft, 10);
33852         var cbot = ctop + el.offsetHeight;
33853         var cright = cleft + el.offsetWidth;
33854         
33855         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33856         var stop = parseInt(c.scrollTop, 10);
33857         var sleft = parseInt(c.scrollLeft, 10);
33858         var sbot = stop + ch;
33859         var sright = sleft + c.clientWidth;
33860         /*
33861         Roo.log('GridView.ensureVisible:' +
33862                 ' ctop:' + ctop +
33863                 ' c.clientHeight:' + c.clientHeight +
33864                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33865                 ' stop:' + stop +
33866                 ' cbot:' + cbot +
33867                 ' sbot:' + sbot +
33868                 ' ch:' + ch  
33869                 );
33870         */
33871         if(ctop < stop){
33872             c.scrollTop = ctop;
33873             //Roo.log("set scrolltop to ctop DISABLE?");
33874         }else if(cbot > sbot){
33875             //Roo.log("set scrolltop to cbot-ch");
33876             c.scrollTop = cbot-ch;
33877         }
33878         
33879         if(hscroll !== false){
33880             if(cleft < sleft){
33881                 c.scrollLeft = cleft;
33882             }else if(cright > sright){
33883                 c.scrollLeft = cright-c.clientWidth;
33884             }
33885         }
33886          
33887         return el;
33888     },
33889
33890     updateColumns : function(){
33891         this.grid.stopEditing();
33892         var cm = this.grid.colModel, colIds = this.getColumnIds();
33893         //var totalWidth = cm.getTotalWidth();
33894         var pos = 0;
33895         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33896             //if(cm.isHidden(i)) continue;
33897             var w = cm.getColumnWidth(i);
33898             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33899             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33900         }
33901         this.updateSplitters();
33902     },
33903
33904     generateRules : function(cm){
33905         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33906         Roo.util.CSS.removeStyleSheet(rulesId);
33907         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33908             var cid = cm.getColumnId(i);
33909             var align = '';
33910             if(cm.config[i].align){
33911                 align = 'text-align:'+cm.config[i].align+';';
33912             }
33913             var hidden = '';
33914             if(cm.isHidden(i)){
33915                 hidden = 'display:none;';
33916             }
33917             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33918             ruleBuf.push(
33919                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33920                     this.hdSelector, cid, " {\n", align, width, "}\n",
33921                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33922                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33923         }
33924         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33925     },
33926
33927     updateSplitters : function(){
33928         var cm = this.cm, s = this.getSplitters();
33929         if(s){ // splitters not created yet
33930             var pos = 0, locked = true;
33931             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33932                 if(cm.isHidden(i)) {
33933                     continue;
33934                 }
33935                 var w = cm.getColumnWidth(i); // make sure it's a number
33936                 if(!cm.isLocked(i) && locked){
33937                     pos = 0;
33938                     locked = false;
33939                 }
33940                 pos += w;
33941                 s[i].style.left = (pos-this.splitOffset) + "px";
33942             }
33943         }
33944     },
33945
33946     handleHiddenChange : function(colModel, colIndex, hidden){
33947         if(hidden){
33948             this.hideColumn(colIndex);
33949         }else{
33950             this.unhideColumn(colIndex);
33951         }
33952     },
33953
33954     hideColumn : function(colIndex){
33955         var cid = this.getColumnId(colIndex);
33956         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33957         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33958         if(Roo.isSafari){
33959             this.updateHeaders();
33960         }
33961         this.updateSplitters();
33962         this.layout();
33963     },
33964
33965     unhideColumn : function(colIndex){
33966         var cid = this.getColumnId(colIndex);
33967         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33968         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33969
33970         if(Roo.isSafari){
33971             this.updateHeaders();
33972         }
33973         this.updateSplitters();
33974         this.layout();
33975     },
33976
33977     insertRows : function(dm, firstRow, lastRow, isUpdate){
33978         if(firstRow == 0 && lastRow == dm.getCount()-1){
33979             this.refresh();
33980         }else{
33981             if(!isUpdate){
33982                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33983             }
33984             var s = this.getScrollState();
33985             var markup = this.renderRows(firstRow, lastRow);
33986             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33987             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33988             this.restoreScroll(s);
33989             if(!isUpdate){
33990                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33991                 this.syncRowHeights(firstRow, lastRow);
33992                 this.stripeRows(firstRow);
33993                 this.layout();
33994             }
33995         }
33996     },
33997
33998     bufferRows : function(markup, target, index){
33999         var before = null, trows = target.rows, tbody = target.tBodies[0];
34000         if(index < trows.length){
34001             before = trows[index];
34002         }
34003         var b = document.createElement("div");
34004         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
34005         var rows = b.firstChild.rows;
34006         for(var i = 0, len = rows.length; i < len; i++){
34007             if(before){
34008                 tbody.insertBefore(rows[0], before);
34009             }else{
34010                 tbody.appendChild(rows[0]);
34011             }
34012         }
34013         b.innerHTML = "";
34014         b = null;
34015     },
34016
34017     deleteRows : function(dm, firstRow, lastRow){
34018         if(dm.getRowCount()<1){
34019             this.fireEvent("beforerefresh", this);
34020             this.mainBody.update("");
34021             this.lockedBody.update("");
34022             this.fireEvent("refresh", this);
34023         }else{
34024             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
34025             var bt = this.getBodyTable();
34026             var tbody = bt.firstChild;
34027             var rows = bt.rows;
34028             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
34029                 tbody.removeChild(rows[firstRow]);
34030             }
34031             this.stripeRows(firstRow);
34032             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
34033         }
34034     },
34035
34036     updateRows : function(dataSource, firstRow, lastRow){
34037         var s = this.getScrollState();
34038         this.refresh();
34039         this.restoreScroll(s);
34040     },
34041
34042     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
34043         if(!noRefresh){
34044            this.refresh();
34045         }
34046         this.updateHeaderSortState();
34047     },
34048
34049     getScrollState : function(){
34050         
34051         var sb = this.scroller.dom;
34052         return {left: sb.scrollLeft, top: sb.scrollTop};
34053     },
34054
34055     stripeRows : function(startRow){
34056         if(!this.grid.stripeRows || this.ds.getCount() < 1){
34057             return;
34058         }
34059         startRow = startRow || 0;
34060         var rows = this.getBodyTable().rows;
34061         var lrows = this.getLockedTable().rows;
34062         var cls = ' x-grid-row-alt ';
34063         for(var i = startRow, len = rows.length; i < len; i++){
34064             var row = rows[i], lrow = lrows[i];
34065             var isAlt = ((i+1) % 2 == 0);
34066             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
34067             if(isAlt == hasAlt){
34068                 continue;
34069             }
34070             if(isAlt){
34071                 row.className += " x-grid-row-alt";
34072             }else{
34073                 row.className = row.className.replace("x-grid-row-alt", "");
34074             }
34075             if(lrow){
34076                 lrow.className = row.className;
34077             }
34078         }
34079     },
34080
34081     restoreScroll : function(state){
34082         //Roo.log('GridView.restoreScroll');
34083         var sb = this.scroller.dom;
34084         sb.scrollLeft = state.left;
34085         sb.scrollTop = state.top;
34086         this.syncScroll();
34087     },
34088
34089     syncScroll : function(){
34090         //Roo.log('GridView.syncScroll');
34091         var sb = this.scroller.dom;
34092         var sh = this.mainHd.dom;
34093         var bs = this.mainBody.dom;
34094         var lv = this.lockedBody.dom;
34095         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
34096         lv.scrollTop = bs.scrollTop = sb.scrollTop;
34097     },
34098
34099     handleScroll : function(e){
34100         this.syncScroll();
34101         var sb = this.scroller.dom;
34102         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
34103         e.stopEvent();
34104     },
34105
34106     handleWheel : function(e){
34107         var d = e.getWheelDelta();
34108         this.scroller.dom.scrollTop -= d*22;
34109         // set this here to prevent jumpy scrolling on large tables
34110         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
34111         e.stopEvent();
34112     },
34113
34114     renderRows : function(startRow, endRow){
34115         // pull in all the crap needed to render rows
34116         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
34117         var colCount = cm.getColumnCount();
34118
34119         if(ds.getCount() < 1){
34120             return ["", ""];
34121         }
34122
34123         // build a map for all the columns
34124         var cs = [];
34125         for(var i = 0; i < colCount; i++){
34126             var name = cm.getDataIndex(i);
34127             cs[i] = {
34128                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
34129                 renderer : cm.getRenderer(i),
34130                 id : cm.getColumnId(i),
34131                 locked : cm.isLocked(i),
34132                 has_editor : cm.isCellEditable(i)
34133             };
34134         }
34135
34136         startRow = startRow || 0;
34137         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
34138
34139         // records to render
34140         var rs = ds.getRange(startRow, endRow);
34141
34142         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
34143     },
34144
34145     // As much as I hate to duplicate code, this was branched because FireFox really hates
34146     // [].join("") on strings. The performance difference was substantial enough to
34147     // branch this function
34148     doRender : Roo.isGecko ?
34149             function(cs, rs, ds, startRow, colCount, stripe){
34150                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34151                 // buffers
34152                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34153                 
34154                 var hasListener = this.grid.hasListener('rowclass');
34155                 var rowcfg = {};
34156                 for(var j = 0, len = rs.length; j < len; j++){
34157                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34158                     for(var i = 0; i < colCount; i++){
34159                         c = cs[i];
34160                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34161                         p.id = c.id;
34162                         p.css = p.attr = "";
34163                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34164                         if(p.value == undefined || p.value === "") {
34165                             p.value = "&#160;";
34166                         }
34167                         if(c.has_editor){
34168                             p.css += ' x-grid-editable-cell';
34169                         }
34170                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
34171                             p.css +=  ' x-grid-dirty-cell';
34172                         }
34173                         var markup = ct.apply(p);
34174                         if(!c.locked){
34175                             cb+= markup;
34176                         }else{
34177                             lcb+= markup;
34178                         }
34179                     }
34180                     var alt = [];
34181                     if(stripe && ((rowIndex+1) % 2 == 0)){
34182                         alt.push("x-grid-row-alt")
34183                     }
34184                     if(r.dirty){
34185                         alt.push(  " x-grid-dirty-row");
34186                     }
34187                     rp.cells = lcb;
34188                     if(this.getRowClass){
34189                         alt.push(this.getRowClass(r, rowIndex));
34190                     }
34191                     if (hasListener) {
34192                         rowcfg = {
34193                              
34194                             record: r,
34195                             rowIndex : rowIndex,
34196                             rowClass : ''
34197                         };
34198                         this.grid.fireEvent('rowclass', this, rowcfg);
34199                         alt.push(rowcfg.rowClass);
34200                     }
34201                     rp.alt = alt.join(" ");
34202                     lbuf+= rt.apply(rp);
34203                     rp.cells = cb;
34204                     buf+=  rt.apply(rp);
34205                 }
34206                 return [lbuf, buf];
34207             } :
34208             function(cs, rs, ds, startRow, colCount, stripe){
34209                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34210                 // buffers
34211                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34212                 var hasListener = this.grid.hasListener('rowclass');
34213  
34214                 var rowcfg = {};
34215                 for(var j = 0, len = rs.length; j < len; j++){
34216                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
34217                     for(var i = 0; i < colCount; i++){
34218                         c = cs[i];
34219                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34220                         p.id = c.id;
34221                         p.css = p.attr = "";
34222                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34223                         if(p.value == undefined || p.value === "") {
34224                             p.value = "&#160;";
34225                         }
34226                         //Roo.log(c);
34227                          if(c.has_editor){
34228                             p.css += ' x-grid-editable-cell';
34229                         }
34230                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34231                             p.css += ' x-grid-dirty-cell' 
34232                         }
34233                         
34234                         var markup = ct.apply(p);
34235                         if(!c.locked){
34236                             cb[cb.length] = markup;
34237                         }else{
34238                             lcb[lcb.length] = markup;
34239                         }
34240                     }
34241                     var alt = [];
34242                     if(stripe && ((rowIndex+1) % 2 == 0)){
34243                         alt.push( "x-grid-row-alt");
34244                     }
34245                     if(r.dirty){
34246                         alt.push(" x-grid-dirty-row");
34247                     }
34248                     rp.cells = lcb;
34249                     if(this.getRowClass){
34250                         alt.push( this.getRowClass(r, rowIndex));
34251                     }
34252                     if (hasListener) {
34253                         rowcfg = {
34254                              
34255                             record: r,
34256                             rowIndex : rowIndex,
34257                             rowClass : ''
34258                         };
34259                         this.grid.fireEvent('rowclass', this, rowcfg);
34260                         alt.push(rowcfg.rowClass);
34261                     }
34262                     
34263                     rp.alt = alt.join(" ");
34264                     rp.cells = lcb.join("");
34265                     lbuf[lbuf.length] = rt.apply(rp);
34266                     rp.cells = cb.join("");
34267                     buf[buf.length] =  rt.apply(rp);
34268                 }
34269                 return [lbuf.join(""), buf.join("")];
34270             },
34271
34272     renderBody : function(){
34273         var markup = this.renderRows();
34274         var bt = this.templates.body;
34275         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34276     },
34277
34278     /**
34279      * Refreshes the grid
34280      * @param {Boolean} headersToo
34281      */
34282     refresh : function(headersToo){
34283         this.fireEvent("beforerefresh", this);
34284         this.grid.stopEditing();
34285         var result = this.renderBody();
34286         this.lockedBody.update(result[0]);
34287         this.mainBody.update(result[1]);
34288         if(headersToo === true){
34289             this.updateHeaders();
34290             this.updateColumns();
34291             this.updateSplitters();
34292             this.updateHeaderSortState();
34293         }
34294         this.syncRowHeights();
34295         this.layout();
34296         this.fireEvent("refresh", this);
34297     },
34298
34299     handleColumnMove : function(cm, oldIndex, newIndex){
34300         this.indexMap = null;
34301         var s = this.getScrollState();
34302         this.refresh(true);
34303         this.restoreScroll(s);
34304         this.afterMove(newIndex);
34305     },
34306
34307     afterMove : function(colIndex){
34308         if(this.enableMoveAnim && Roo.enableFx){
34309             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34310         }
34311         // if multisort - fix sortOrder, and reload..
34312         if (this.grid.dataSource.multiSort) {
34313             // the we can call sort again..
34314             var dm = this.grid.dataSource;
34315             var cm = this.grid.colModel;
34316             var so = [];
34317             for(var i = 0; i < cm.config.length; i++ ) {
34318                 
34319                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34320                     continue; // dont' bother, it's not in sort list or being set.
34321                 }
34322                 
34323                 so.push(cm.config[i].dataIndex);
34324             };
34325             dm.sortOrder = so;
34326             dm.load(dm.lastOptions);
34327             
34328             
34329         }
34330         
34331     },
34332
34333     updateCell : function(dm, rowIndex, dataIndex){
34334         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34335         if(typeof colIndex == "undefined"){ // not present in grid
34336             return;
34337         }
34338         var cm = this.grid.colModel;
34339         var cell = this.getCell(rowIndex, colIndex);
34340         var cellText = this.getCellText(rowIndex, colIndex);
34341
34342         var p = {
34343             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34344             id : cm.getColumnId(colIndex),
34345             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34346         };
34347         var renderer = cm.getRenderer(colIndex);
34348         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34349         if(typeof val == "undefined" || val === "") {
34350             val = "&#160;";
34351         }
34352         cellText.innerHTML = val;
34353         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34354         this.syncRowHeights(rowIndex, rowIndex);
34355     },
34356
34357     calcColumnWidth : function(colIndex, maxRowsToMeasure){
34358         var maxWidth = 0;
34359         if(this.grid.autoSizeHeaders){
34360             var h = this.getHeaderCellMeasure(colIndex);
34361             maxWidth = Math.max(maxWidth, h.scrollWidth);
34362         }
34363         var tb, index;
34364         if(this.cm.isLocked(colIndex)){
34365             tb = this.getLockedTable();
34366             index = colIndex;
34367         }else{
34368             tb = this.getBodyTable();
34369             index = colIndex - this.cm.getLockedCount();
34370         }
34371         if(tb && tb.rows){
34372             var rows = tb.rows;
34373             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34374             for(var i = 0; i < stopIndex; i++){
34375                 var cell = rows[i].childNodes[index].firstChild;
34376                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34377             }
34378         }
34379         return maxWidth + /*margin for error in IE*/ 5;
34380     },
34381     /**
34382      * Autofit a column to its content.
34383      * @param {Number} colIndex
34384      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34385      */
34386      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34387          if(this.cm.isHidden(colIndex)){
34388              return; // can't calc a hidden column
34389          }
34390         if(forceMinSize){
34391             var cid = this.cm.getColumnId(colIndex);
34392             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34393            if(this.grid.autoSizeHeaders){
34394                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34395            }
34396         }
34397         var newWidth = this.calcColumnWidth(colIndex);
34398         this.cm.setColumnWidth(colIndex,
34399             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34400         if(!suppressEvent){
34401             this.grid.fireEvent("columnresize", colIndex, newWidth);
34402         }
34403     },
34404
34405     /**
34406      * Autofits all columns to their content and then expands to fit any extra space in the grid
34407      */
34408      autoSizeColumns : function(){
34409         var cm = this.grid.colModel;
34410         var colCount = cm.getColumnCount();
34411         for(var i = 0; i < colCount; i++){
34412             this.autoSizeColumn(i, true, true);
34413         }
34414         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
34415             this.fitColumns();
34416         }else{
34417             this.updateColumns();
34418             this.layout();
34419         }
34420     },
34421
34422     /**
34423      * Autofits all columns to the grid's width proportionate with their current size
34424      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
34425      */
34426     fitColumns : function(reserveScrollSpace){
34427         var cm = this.grid.colModel;
34428         var colCount = cm.getColumnCount();
34429         var cols = [];
34430         var width = 0;
34431         var i, w;
34432         for (i = 0; i < colCount; i++){
34433             if(!cm.isHidden(i) && !cm.isFixed(i)){
34434                 w = cm.getColumnWidth(i);
34435                 cols.push(i);
34436                 cols.push(w);
34437                 width += w;
34438             }
34439         }
34440         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
34441         if(reserveScrollSpace){
34442             avail -= 17;
34443         }
34444         var frac = (avail - cm.getTotalWidth())/width;
34445         while (cols.length){
34446             w = cols.pop();
34447             i = cols.pop();
34448             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
34449         }
34450         this.updateColumns();
34451         this.layout();
34452     },
34453
34454     onRowSelect : function(rowIndex){
34455         var row = this.getRowComposite(rowIndex);
34456         row.addClass("x-grid-row-selected");
34457     },
34458
34459     onRowDeselect : function(rowIndex){
34460         var row = this.getRowComposite(rowIndex);
34461         row.removeClass("x-grid-row-selected");
34462     },
34463
34464     onCellSelect : function(row, col){
34465         var cell = this.getCell(row, col);
34466         if(cell){
34467             Roo.fly(cell).addClass("x-grid-cell-selected");
34468         }
34469     },
34470
34471     onCellDeselect : function(row, col){
34472         var cell = this.getCell(row, col);
34473         if(cell){
34474             Roo.fly(cell).removeClass("x-grid-cell-selected");
34475         }
34476     },
34477
34478     updateHeaderSortState : function(){
34479         
34480         // sort state can be single { field: xxx, direction : yyy}
34481         // or   { xxx=>ASC , yyy : DESC ..... }
34482         
34483         var mstate = {};
34484         if (!this.ds.multiSort) { 
34485             var state = this.ds.getSortState();
34486             if(!state){
34487                 return;
34488             }
34489             mstate[state.field] = state.direction;
34490             // FIXME... - this is not used here.. but might be elsewhere..
34491             this.sortState = state;
34492             
34493         } else {
34494             mstate = this.ds.sortToggle;
34495         }
34496         //remove existing sort classes..
34497         
34498         var sc = this.sortClasses;
34499         var hds = this.el.select(this.headerSelector).removeClass(sc);
34500         
34501         for(var f in mstate) {
34502         
34503             var sortColumn = this.cm.findColumnIndex(f);
34504             
34505             if(sortColumn != -1){
34506                 var sortDir = mstate[f];        
34507                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34508             }
34509         }
34510         
34511          
34512         
34513     },
34514
34515
34516     handleHeaderClick : function(g, index,e){
34517         
34518         Roo.log("header click");
34519         
34520         if (Roo.isTouch) {
34521             // touch events on header are handled by context
34522             this.handleHdCtx(g,index,e);
34523             return;
34524         }
34525         
34526         
34527         if(this.headersDisabled){
34528             return;
34529         }
34530         var dm = g.dataSource, cm = g.colModel;
34531         if(!cm.isSortable(index)){
34532             return;
34533         }
34534         g.stopEditing();
34535         
34536         if (dm.multiSort) {
34537             // update the sortOrder
34538             var so = [];
34539             for(var i = 0; i < cm.config.length; i++ ) {
34540                 
34541                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34542                     continue; // dont' bother, it's not in sort list or being set.
34543                 }
34544                 
34545                 so.push(cm.config[i].dataIndex);
34546             };
34547             dm.sortOrder = so;
34548         }
34549         
34550         
34551         dm.sort(cm.getDataIndex(index));
34552     },
34553
34554
34555     destroy : function(){
34556         if(this.colMenu){
34557             this.colMenu.removeAll();
34558             Roo.menu.MenuMgr.unregister(this.colMenu);
34559             this.colMenu.getEl().remove();
34560             delete this.colMenu;
34561         }
34562         if(this.hmenu){
34563             this.hmenu.removeAll();
34564             Roo.menu.MenuMgr.unregister(this.hmenu);
34565             this.hmenu.getEl().remove();
34566             delete this.hmenu;
34567         }
34568         if(this.grid.enableColumnMove){
34569             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34570             if(dds){
34571                 for(var dd in dds){
34572                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34573                         var elid = dds[dd].dragElId;
34574                         dds[dd].unreg();
34575                         Roo.get(elid).remove();
34576                     } else if(dds[dd].config.isTarget){
34577                         dds[dd].proxyTop.remove();
34578                         dds[dd].proxyBottom.remove();
34579                         dds[dd].unreg();
34580                     }
34581                     if(Roo.dd.DDM.locationCache[dd]){
34582                         delete Roo.dd.DDM.locationCache[dd];
34583                     }
34584                 }
34585                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34586             }
34587         }
34588         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34589         this.bind(null, null);
34590         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34591     },
34592
34593     handleLockChange : function(){
34594         this.refresh(true);
34595     },
34596
34597     onDenyColumnLock : function(){
34598
34599     },
34600
34601     onDenyColumnHide : function(){
34602
34603     },
34604
34605     handleHdMenuClick : function(item){
34606         var index = this.hdCtxIndex;
34607         var cm = this.cm, ds = this.ds;
34608         switch(item.id){
34609             case "asc":
34610                 ds.sort(cm.getDataIndex(index), "ASC");
34611                 break;
34612             case "desc":
34613                 ds.sort(cm.getDataIndex(index), "DESC");
34614                 break;
34615             case "lock":
34616                 var lc = cm.getLockedCount();
34617                 if(cm.getColumnCount(true) <= lc+1){
34618                     this.onDenyColumnLock();
34619                     return;
34620                 }
34621                 if(lc != index){
34622                     cm.setLocked(index, true, true);
34623                     cm.moveColumn(index, lc);
34624                     this.grid.fireEvent("columnmove", index, lc);
34625                 }else{
34626                     cm.setLocked(index, true);
34627                 }
34628             break;
34629             case "unlock":
34630                 var lc = cm.getLockedCount();
34631                 if((lc-1) != index){
34632                     cm.setLocked(index, false, true);
34633                     cm.moveColumn(index, lc-1);
34634                     this.grid.fireEvent("columnmove", index, lc-1);
34635                 }else{
34636                     cm.setLocked(index, false);
34637                 }
34638             break;
34639             case 'wider': // used to expand cols on touch..
34640             case 'narrow':
34641                 var cw = cm.getColumnWidth(index);
34642                 cw += (item.id == 'wider' ? 1 : -1) * 50;
34643                 cw = Math.max(0, cw);
34644                 cw = Math.min(cw,4000);
34645                 cm.setColumnWidth(index, cw);
34646                 break;
34647                 
34648             default:
34649                 index = cm.getIndexById(item.id.substr(4));
34650                 if(index != -1){
34651                     if(item.checked && cm.getColumnCount(true) <= 1){
34652                         this.onDenyColumnHide();
34653                         return false;
34654                     }
34655                     cm.setHidden(index, item.checked);
34656                 }
34657         }
34658         return true;
34659     },
34660
34661     beforeColMenuShow : function(){
34662         var cm = this.cm,  colCount = cm.getColumnCount();
34663         this.colMenu.removeAll();
34664         for(var i = 0; i < colCount; i++){
34665             this.colMenu.add(new Roo.menu.CheckItem({
34666                 id: "col-"+cm.getColumnId(i),
34667                 text: cm.getColumnHeader(i),
34668                 checked: !cm.isHidden(i),
34669                 hideOnClick:false
34670             }));
34671         }
34672     },
34673
34674     handleHdCtx : function(g, index, e){
34675         e.stopEvent();
34676         var hd = this.getHeaderCell(index);
34677         this.hdCtxIndex = index;
34678         var ms = this.hmenu.items, cm = this.cm;
34679         ms.get("asc").setDisabled(!cm.isSortable(index));
34680         ms.get("desc").setDisabled(!cm.isSortable(index));
34681         if(this.grid.enableColLock !== false){
34682             ms.get("lock").setDisabled(cm.isLocked(index));
34683             ms.get("unlock").setDisabled(!cm.isLocked(index));
34684         }
34685         this.hmenu.show(hd, "tl-bl");
34686     },
34687
34688     handleHdOver : function(e){
34689         var hd = this.findHeaderCell(e.getTarget());
34690         if(hd && !this.headersDisabled){
34691             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34692                this.fly(hd).addClass("x-grid-hd-over");
34693             }
34694         }
34695     },
34696
34697     handleHdOut : function(e){
34698         var hd = this.findHeaderCell(e.getTarget());
34699         if(hd){
34700             this.fly(hd).removeClass("x-grid-hd-over");
34701         }
34702     },
34703
34704     handleSplitDblClick : function(e, t){
34705         var i = this.getCellIndex(t);
34706         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34707             this.autoSizeColumn(i, true);
34708             this.layout();
34709         }
34710     },
34711
34712     render : function(){
34713
34714         var cm = this.cm;
34715         var colCount = cm.getColumnCount();
34716
34717         if(this.grid.monitorWindowResize === true){
34718             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34719         }
34720         var header = this.renderHeaders();
34721         var body = this.templates.body.apply({rows:""});
34722         var html = this.templates.master.apply({
34723             lockedBody: body,
34724             body: body,
34725             lockedHeader: header[0],
34726             header: header[1]
34727         });
34728
34729         //this.updateColumns();
34730
34731         this.grid.getGridEl().dom.innerHTML = html;
34732
34733         this.initElements();
34734         
34735         // a kludge to fix the random scolling effect in webkit
34736         this.el.on("scroll", function() {
34737             this.el.dom.scrollTop=0; // hopefully not recursive..
34738         },this);
34739
34740         this.scroller.on("scroll", this.handleScroll, this);
34741         this.lockedBody.on("mousewheel", this.handleWheel, this);
34742         this.mainBody.on("mousewheel", this.handleWheel, this);
34743
34744         this.mainHd.on("mouseover", this.handleHdOver, this);
34745         this.mainHd.on("mouseout", this.handleHdOut, this);
34746         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34747                 {delegate: "."+this.splitClass});
34748
34749         this.lockedHd.on("mouseover", this.handleHdOver, this);
34750         this.lockedHd.on("mouseout", this.handleHdOut, this);
34751         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34752                 {delegate: "."+this.splitClass});
34753
34754         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34755             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34756         }
34757
34758         this.updateSplitters();
34759
34760         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34761             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34762             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34763         }
34764
34765         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34766             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34767             this.hmenu.add(
34768                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34769                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34770             );
34771             if(this.grid.enableColLock !== false){
34772                 this.hmenu.add('-',
34773                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34774                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34775                 );
34776             }
34777             if (Roo.isTouch) {
34778                  this.hmenu.add('-',
34779                     {id:"wider", text: this.columnsWiderText},
34780                     {id:"narrow", text: this.columnsNarrowText }
34781                 );
34782                 
34783                  
34784             }
34785             
34786             if(this.grid.enableColumnHide !== false){
34787
34788                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34789                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34790                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34791
34792                 this.hmenu.add('-',
34793                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34794                 );
34795             }
34796             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34797
34798             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34799         }
34800
34801         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34802             this.dd = new Roo.grid.GridDragZone(this.grid, {
34803                 ddGroup : this.grid.ddGroup || 'GridDD'
34804             });
34805             
34806         }
34807
34808         /*
34809         for(var i = 0; i < colCount; i++){
34810             if(cm.isHidden(i)){
34811                 this.hideColumn(i);
34812             }
34813             if(cm.config[i].align){
34814                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34815                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34816             }
34817         }*/
34818         
34819         this.updateHeaderSortState();
34820
34821         this.beforeInitialResize();
34822         this.layout(true);
34823
34824         // two part rendering gives faster view to the user
34825         this.renderPhase2.defer(1, this);
34826     },
34827
34828     renderPhase2 : function(){
34829         // render the rows now
34830         this.refresh();
34831         if(this.grid.autoSizeColumns){
34832             this.autoSizeColumns();
34833         }
34834     },
34835
34836     beforeInitialResize : function(){
34837
34838     },
34839
34840     onColumnSplitterMoved : function(i, w){
34841         this.userResized = true;
34842         var cm = this.grid.colModel;
34843         cm.setColumnWidth(i, w, true);
34844         var cid = cm.getColumnId(i);
34845         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34846         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34847         this.updateSplitters();
34848         this.layout();
34849         this.grid.fireEvent("columnresize", i, w);
34850     },
34851
34852     syncRowHeights : function(startIndex, endIndex){
34853         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34854             startIndex = startIndex || 0;
34855             var mrows = this.getBodyTable().rows;
34856             var lrows = this.getLockedTable().rows;
34857             var len = mrows.length-1;
34858             endIndex = Math.min(endIndex || len, len);
34859             for(var i = startIndex; i <= endIndex; i++){
34860                 var m = mrows[i], l = lrows[i];
34861                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34862                 m.style.height = l.style.height = h + "px";
34863             }
34864         }
34865     },
34866
34867     layout : function(initialRender, is2ndPass)
34868     {
34869         var g = this.grid;
34870         var auto = g.autoHeight;
34871         var scrollOffset = 16;
34872         var c = g.getGridEl(), cm = this.cm,
34873                 expandCol = g.autoExpandColumn,
34874                 gv = this;
34875         //c.beginMeasure();
34876
34877         if(!c.dom.offsetWidth){ // display:none?
34878             if(initialRender){
34879                 this.lockedWrap.show();
34880                 this.mainWrap.show();
34881             }
34882             return;
34883         }
34884
34885         var hasLock = this.cm.isLocked(0);
34886
34887         var tbh = this.headerPanel.getHeight();
34888         var bbh = this.footerPanel.getHeight();
34889
34890         if(auto){
34891             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34892             var newHeight = ch + c.getBorderWidth("tb");
34893             if(g.maxHeight){
34894                 newHeight = Math.min(g.maxHeight, newHeight);
34895             }
34896             c.setHeight(newHeight);
34897         }
34898
34899         if(g.autoWidth){
34900             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34901         }
34902
34903         var s = this.scroller;
34904
34905         var csize = c.getSize(true);
34906
34907         this.el.setSize(csize.width, csize.height);
34908
34909         this.headerPanel.setWidth(csize.width);
34910         this.footerPanel.setWidth(csize.width);
34911
34912         var hdHeight = this.mainHd.getHeight();
34913         var vw = csize.width;
34914         var vh = csize.height - (tbh + bbh);
34915
34916         s.setSize(vw, vh);
34917
34918         var bt = this.getBodyTable();
34919         
34920         if(cm.getLockedCount() == cm.config.length){
34921             bt = this.getLockedTable();
34922         }
34923         
34924         var ltWidth = hasLock ?
34925                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34926
34927         var scrollHeight = bt.offsetHeight;
34928         var scrollWidth = ltWidth + bt.offsetWidth;
34929         var vscroll = false, hscroll = false;
34930
34931         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34932
34933         var lw = this.lockedWrap, mw = this.mainWrap;
34934         var lb = this.lockedBody, mb = this.mainBody;
34935
34936         setTimeout(function(){
34937             var t = s.dom.offsetTop;
34938             var w = s.dom.clientWidth,
34939                 h = s.dom.clientHeight;
34940
34941             lw.setTop(t);
34942             lw.setSize(ltWidth, h);
34943
34944             mw.setLeftTop(ltWidth, t);
34945             mw.setSize(w-ltWidth, h);
34946
34947             lb.setHeight(h-hdHeight);
34948             mb.setHeight(h-hdHeight);
34949
34950             if(is2ndPass !== true && !gv.userResized && expandCol){
34951                 // high speed resize without full column calculation
34952                 
34953                 var ci = cm.getIndexById(expandCol);
34954                 if (ci < 0) {
34955                     ci = cm.findColumnIndex(expandCol);
34956                 }
34957                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34958                 var expandId = cm.getColumnId(ci);
34959                 var  tw = cm.getTotalWidth(false);
34960                 var currentWidth = cm.getColumnWidth(ci);
34961                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34962                 if(currentWidth != cw){
34963                     cm.setColumnWidth(ci, cw, true);
34964                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34965                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34966                     gv.updateSplitters();
34967                     gv.layout(false, true);
34968                 }
34969             }
34970
34971             if(initialRender){
34972                 lw.show();
34973                 mw.show();
34974             }
34975             //c.endMeasure();
34976         }, 10);
34977     },
34978
34979     onWindowResize : function(){
34980         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34981             return;
34982         }
34983         this.layout();
34984     },
34985
34986     appendFooter : function(parentEl){
34987         return null;
34988     },
34989
34990     sortAscText : "Sort Ascending",
34991     sortDescText : "Sort Descending",
34992     lockText : "Lock Column",
34993     unlockText : "Unlock Column",
34994     columnsText : "Columns",
34995  
34996     columnsWiderText : "Wider",
34997     columnsNarrowText : "Thinner"
34998 });
34999
35000
35001 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
35002     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
35003     this.proxy.el.addClass('x-grid3-col-dd');
35004 };
35005
35006 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
35007     handleMouseDown : function(e){
35008
35009     },
35010
35011     callHandleMouseDown : function(e){
35012         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
35013     }
35014 });
35015 /*
35016  * Based on:
35017  * Ext JS Library 1.1.1
35018  * Copyright(c) 2006-2007, Ext JS, LLC.
35019  *
35020  * Originally Released Under LGPL - original licence link has changed is not relivant.
35021  *
35022  * Fork - LGPL
35023  * <script type="text/javascript">
35024  */
35025  /**
35026  * @extends Roo.dd.DDProxy
35027  * @class Roo.grid.SplitDragZone
35028  * Support for Column Header resizing
35029  * @constructor
35030  * @param {Object} config
35031  */
35032 // private
35033 // This is a support class used internally by the Grid components
35034 Roo.grid.SplitDragZone = function(grid, hd, hd2){
35035     this.grid = grid;
35036     this.view = grid.getView();
35037     this.proxy = this.view.resizeProxy;
35038     Roo.grid.SplitDragZone.superclass.constructor.call(
35039         this,
35040         hd, // ID
35041         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
35042         {  // CONFIG
35043             dragElId : Roo.id(this.proxy.dom),
35044             resizeFrame:false
35045         }
35046     );
35047     
35048     this.setHandleElId(Roo.id(hd));
35049     if (hd2 !== false) {
35050         this.setOuterHandleElId(Roo.id(hd2));
35051     }
35052     
35053     this.scroll = false;
35054 };
35055 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
35056     fly: Roo.Element.fly,
35057
35058     b4StartDrag : function(x, y){
35059         this.view.headersDisabled = true;
35060         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
35061                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
35062         );
35063         this.proxy.setHeight(h);
35064         
35065         // for old system colWidth really stored the actual width?
35066         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
35067         // which in reality did not work.. - it worked only for fixed sizes
35068         // for resizable we need to use actual sizes.
35069         var w = this.cm.getColumnWidth(this.cellIndex);
35070         if (!this.view.mainWrap) {
35071             // bootstrap.
35072             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
35073         }
35074         
35075         
35076         
35077         // this was w-this.grid.minColumnWidth;
35078         // doesnt really make sense? - w = thie curren width or the rendered one?
35079         var minw = Math.max(w-this.grid.minColumnWidth, 0);
35080         this.resetConstraints();
35081         this.setXConstraint(minw, 1000);
35082         this.setYConstraint(0, 0);
35083         this.minX = x - minw;
35084         this.maxX = x + 1000;
35085         this.startPos = x;
35086         if (!this.view.mainWrap) { // this is Bootstrap code..
35087             this.getDragEl().style.display='block';
35088         }
35089         
35090         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
35091     },
35092
35093
35094     handleMouseDown : function(e){
35095         ev = Roo.EventObject.setEvent(e);
35096         var t = this.fly(ev.getTarget());
35097         if(t.hasClass("x-grid-split")){
35098             this.cellIndex = this.view.getCellIndex(t.dom);
35099             this.split = t.dom;
35100             this.cm = this.grid.colModel;
35101             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
35102                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
35103             }
35104         }
35105     },
35106
35107     endDrag : function(e){
35108         this.view.headersDisabled = false;
35109         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
35110         var diff = endX - this.startPos;
35111         // 
35112         var w = this.cm.getColumnWidth(this.cellIndex);
35113         if (!this.view.mainWrap) {
35114             w = 0;
35115         }
35116         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
35117     },
35118
35119     autoOffset : function(){
35120         this.setDelta(0,0);
35121     }
35122 });/*
35123  * Based on:
35124  * Ext JS Library 1.1.1
35125  * Copyright(c) 2006-2007, Ext JS, LLC.
35126  *
35127  * Originally Released Under LGPL - original licence link has changed is not relivant.
35128  *
35129  * Fork - LGPL
35130  * <script type="text/javascript">
35131  */
35132  
35133 // private
35134 // This is a support class used internally by the Grid components
35135 Roo.grid.GridDragZone = function(grid, config){
35136     this.view = grid.getView();
35137     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
35138     if(this.view.lockedBody){
35139         this.setHandleElId(Roo.id(this.view.mainBody.dom));
35140         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
35141     }
35142     this.scroll = false;
35143     this.grid = grid;
35144     this.ddel = document.createElement('div');
35145     this.ddel.className = 'x-grid-dd-wrap';
35146 };
35147
35148 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
35149     ddGroup : "GridDD",
35150
35151     getDragData : function(e){
35152         var t = Roo.lib.Event.getTarget(e);
35153         var rowIndex = this.view.findRowIndex(t);
35154         var sm = this.grid.selModel;
35155             
35156         //Roo.log(rowIndex);
35157         
35158         if (sm.getSelectedCell) {
35159             // cell selection..
35160             if (!sm.getSelectedCell()) {
35161                 return false;
35162             }
35163             if (rowIndex != sm.getSelectedCell()[0]) {
35164                 return false;
35165             }
35166         
35167         }
35168         if (sm.getSelections && sm.getSelections().length < 1) {
35169             return false;
35170         }
35171         
35172         
35173         // before it used to all dragging of unseleted... - now we dont do that.
35174         if(rowIndex !== false){
35175             
35176             // if editorgrid.. 
35177             
35178             
35179             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
35180                
35181             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
35182               //  
35183             //}
35184             if (e.hasModifier()){
35185                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
35186             }
35187             
35188             Roo.log("getDragData");
35189             
35190             return {
35191                 grid: this.grid,
35192                 ddel: this.ddel,
35193                 rowIndex: rowIndex,
35194                 selections: sm.getSelections ? sm.getSelections() : (
35195                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
35196             };
35197         }
35198         return false;
35199     },
35200     
35201     
35202     onInitDrag : function(e){
35203         var data = this.dragData;
35204         this.ddel.innerHTML = this.grid.getDragDropText();
35205         this.proxy.update(this.ddel);
35206         // fire start drag?
35207     },
35208
35209     afterRepair : function(){
35210         this.dragging = false;
35211     },
35212
35213     getRepairXY : function(e, data){
35214         return false;
35215     },
35216
35217     onEndDrag : function(data, e){
35218         // fire end drag?
35219     },
35220
35221     onValidDrop : function(dd, e, id){
35222         // fire drag drop?
35223         this.hideProxy();
35224     },
35225
35226     beforeInvalidDrop : function(e, id){
35227
35228     }
35229 });/*
35230  * Based on:
35231  * Ext JS Library 1.1.1
35232  * Copyright(c) 2006-2007, Ext JS, LLC.
35233  *
35234  * Originally Released Under LGPL - original licence link has changed is not relivant.
35235  *
35236  * Fork - LGPL
35237  * <script type="text/javascript">
35238  */
35239  
35240
35241 /**
35242  * @class Roo.grid.ColumnModel
35243  * @extends Roo.util.Observable
35244  * This is the default implementation of a ColumnModel used by the Grid. It defines
35245  * the columns in the grid.
35246  * <br>Usage:<br>
35247  <pre><code>
35248  var colModel = new Roo.grid.ColumnModel([
35249         {header: "Ticker", width: 60, sortable: true, locked: true},
35250         {header: "Company Name", width: 150, sortable: true},
35251         {header: "Market Cap.", width: 100, sortable: true},
35252         {header: "$ Sales", width: 100, sortable: true, renderer: money},
35253         {header: "Employees", width: 100, sortable: true, resizable: false}
35254  ]);
35255  </code></pre>
35256  * <p>
35257  
35258  * The config options listed for this class are options which may appear in each
35259  * individual column definition.
35260  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35261  * @constructor
35262  * @param {Object} config An Array of column config objects. See this class's
35263  * config objects for details.
35264 */
35265 Roo.grid.ColumnModel = function(config){
35266         /**
35267      * The config passed into the constructor
35268      */
35269     this.config = []; //config;
35270     this.lookup = {};
35271
35272     // if no id, create one
35273     // if the column does not have a dataIndex mapping,
35274     // map it to the order it is in the config
35275     for(var i = 0, len = config.length; i < len; i++){
35276         this.addColumn(config[i]);
35277         
35278     }
35279
35280     /**
35281      * The width of columns which have no width specified (defaults to 100)
35282      * @type Number
35283      */
35284     this.defaultWidth = 100;
35285
35286     /**
35287      * Default sortable of columns which have no sortable specified (defaults to false)
35288      * @type Boolean
35289      */
35290     this.defaultSortable = false;
35291
35292     this.addEvents({
35293         /**
35294              * @event widthchange
35295              * Fires when the width of a column changes.
35296              * @param {ColumnModel} this
35297              * @param {Number} columnIndex The column index
35298              * @param {Number} newWidth The new width
35299              */
35300             "widthchange": true,
35301         /**
35302              * @event headerchange
35303              * Fires when the text of a header changes.
35304              * @param {ColumnModel} this
35305              * @param {Number} columnIndex The column index
35306              * @param {Number} newText The new header text
35307              */
35308             "headerchange": true,
35309         /**
35310              * @event hiddenchange
35311              * Fires when a column is hidden or "unhidden".
35312              * @param {ColumnModel} this
35313              * @param {Number} columnIndex The column index
35314              * @param {Boolean} hidden true if hidden, false otherwise
35315              */
35316             "hiddenchange": true,
35317             /**
35318          * @event columnmoved
35319          * Fires when a column is moved.
35320          * @param {ColumnModel} this
35321          * @param {Number} oldIndex
35322          * @param {Number} newIndex
35323          */
35324         "columnmoved" : true,
35325         /**
35326          * @event columlockchange
35327          * Fires when a column's locked state is changed
35328          * @param {ColumnModel} this
35329          * @param {Number} colIndex
35330          * @param {Boolean} locked true if locked
35331          */
35332         "columnlockchange" : true
35333     });
35334     Roo.grid.ColumnModel.superclass.constructor.call(this);
35335 };
35336 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35337     /**
35338      * @cfg {String} header The header text to display in the Grid view.
35339      */
35340         /**
35341      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
35342      */
35343         /**
35344      * @cfg {String} smHeader Header at Bootsrap Small width
35345      */
35346         /**
35347      * @cfg {String} mdHeader Header at Bootsrap Medium width
35348      */
35349         /**
35350      * @cfg {String} lgHeader Header at Bootsrap Large width
35351      */
35352         /**
35353      * @cfg {String} xlHeader Header at Bootsrap extra Large width
35354      */
35355     /**
35356      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35357      * {@link Roo.data.Record} definition from which to draw the column's value. If not
35358      * specified, the column's index is used as an index into the Record's data Array.
35359      */
35360     /**
35361      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35362      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35363      */
35364     /**
35365      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35366      * Defaults to the value of the {@link #defaultSortable} property.
35367      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35368      */
35369     /**
35370      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
35371      */
35372     /**
35373      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
35374      */
35375     /**
35376      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35377      */
35378     /**
35379      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35380      */
35381     /**
35382      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35383      * given the cell's data value. See {@link #setRenderer}. If not specified, the
35384      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
35385      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
35386      */
35387        /**
35388      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
35389      */
35390     /**
35391      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
35392      */
35393     /**
35394      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
35395      */
35396     /**
35397      * @cfg {String} cursor (Optional)
35398      */
35399     /**
35400      * @cfg {String} tooltip (Optional)
35401      */
35402     /**
35403      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
35404      */
35405     /**
35406      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
35407      */
35408     /**
35409      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
35410      */
35411     /**
35412      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
35413      */
35414         /**
35415      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
35416      */
35417     /**
35418      * Returns the id of the column at the specified index.
35419      * @param {Number} index The column index
35420      * @return {String} the id
35421      */
35422     getColumnId : function(index){
35423         return this.config[index].id;
35424     },
35425
35426     /**
35427      * Returns the column for a specified id.
35428      * @param {String} id The column id
35429      * @return {Object} the column
35430      */
35431     getColumnById : function(id){
35432         return this.lookup[id];
35433     },
35434
35435     
35436     /**
35437      * Returns the column Object for a specified dataIndex.
35438      * @param {String} dataIndex The column dataIndex
35439      * @return {Object|Boolean} the column or false if not found
35440      */
35441     getColumnByDataIndex: function(dataIndex){
35442         var index = this.findColumnIndex(dataIndex);
35443         return index > -1 ? this.config[index] : false;
35444     },
35445     
35446     /**
35447      * Returns the index for a specified column id.
35448      * @param {String} id The column id
35449      * @return {Number} the index, or -1 if not found
35450      */
35451     getIndexById : function(id){
35452         for(var i = 0, len = this.config.length; i < len; i++){
35453             if(this.config[i].id == id){
35454                 return i;
35455             }
35456         }
35457         return -1;
35458     },
35459     
35460     /**
35461      * Returns the index for a specified column dataIndex.
35462      * @param {String} dataIndex The column dataIndex
35463      * @return {Number} the index, or -1 if not found
35464      */
35465     
35466     findColumnIndex : function(dataIndex){
35467         for(var i = 0, len = this.config.length; i < len; i++){
35468             if(this.config[i].dataIndex == dataIndex){
35469                 return i;
35470             }
35471         }
35472         return -1;
35473     },
35474     
35475     
35476     moveColumn : function(oldIndex, newIndex){
35477         var c = this.config[oldIndex];
35478         this.config.splice(oldIndex, 1);
35479         this.config.splice(newIndex, 0, c);
35480         this.dataMap = null;
35481         this.fireEvent("columnmoved", this, oldIndex, newIndex);
35482     },
35483
35484     isLocked : function(colIndex){
35485         return this.config[colIndex].locked === true;
35486     },
35487
35488     setLocked : function(colIndex, value, suppressEvent){
35489         if(this.isLocked(colIndex) == value){
35490             return;
35491         }
35492         this.config[colIndex].locked = value;
35493         if(!suppressEvent){
35494             this.fireEvent("columnlockchange", this, colIndex, value);
35495         }
35496     },
35497
35498     getTotalLockedWidth : function(){
35499         var totalWidth = 0;
35500         for(var i = 0; i < this.config.length; i++){
35501             if(this.isLocked(i) && !this.isHidden(i)){
35502                 this.totalWidth += this.getColumnWidth(i);
35503             }
35504         }
35505         return totalWidth;
35506     },
35507
35508     getLockedCount : function(){
35509         for(var i = 0, len = this.config.length; i < len; i++){
35510             if(!this.isLocked(i)){
35511                 return i;
35512             }
35513         }
35514         
35515         return this.config.length;
35516     },
35517
35518     /**
35519      * Returns the number of columns.
35520      * @return {Number}
35521      */
35522     getColumnCount : function(visibleOnly){
35523         if(visibleOnly === true){
35524             var c = 0;
35525             for(var i = 0, len = this.config.length; i < len; i++){
35526                 if(!this.isHidden(i)){
35527                     c++;
35528                 }
35529             }
35530             return c;
35531         }
35532         return this.config.length;
35533     },
35534
35535     /**
35536      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35537      * @param {Function} fn
35538      * @param {Object} scope (optional)
35539      * @return {Array} result
35540      */
35541     getColumnsBy : function(fn, scope){
35542         var r = [];
35543         for(var i = 0, len = this.config.length; i < len; i++){
35544             var c = this.config[i];
35545             if(fn.call(scope||this, c, i) === true){
35546                 r[r.length] = c;
35547             }
35548         }
35549         return r;
35550     },
35551
35552     /**
35553      * Returns true if the specified column is sortable.
35554      * @param {Number} col The column index
35555      * @return {Boolean}
35556      */
35557     isSortable : function(col){
35558         if(typeof this.config[col].sortable == "undefined"){
35559             return this.defaultSortable;
35560         }
35561         return this.config[col].sortable;
35562     },
35563
35564     /**
35565      * Returns the rendering (formatting) function defined for the column.
35566      * @param {Number} col The column index.
35567      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35568      */
35569     getRenderer : function(col){
35570         if(!this.config[col].renderer){
35571             return Roo.grid.ColumnModel.defaultRenderer;
35572         }
35573         return this.config[col].renderer;
35574     },
35575
35576     /**
35577      * Sets the rendering (formatting) function for a column.
35578      * @param {Number} col The column index
35579      * @param {Function} fn The function to use to process the cell's raw data
35580      * to return HTML markup for the grid view. The render function is called with
35581      * the following parameters:<ul>
35582      * <li>Data value.</li>
35583      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35584      * <li>css A CSS style string to apply to the table cell.</li>
35585      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35586      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35587      * <li>Row index</li>
35588      * <li>Column index</li>
35589      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35590      */
35591     setRenderer : function(col, fn){
35592         this.config[col].renderer = fn;
35593     },
35594
35595     /**
35596      * Returns the width for the specified column.
35597      * @param {Number} col The column index
35598      * @param (optional) {String} gridSize bootstrap width size.
35599      * @return {Number}
35600      */
35601     getColumnWidth : function(col, gridSize)
35602         {
35603                 var cfg = this.config[col];
35604                 
35605                 if (typeof(gridSize) == 'undefined') {
35606                         return cfg.width * 1 || this.defaultWidth;
35607                 }
35608                 if (gridSize === false) { // if we set it..
35609                         return cfg.width || false;
35610                 }
35611                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
35612                 
35613                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
35614                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
35615                                 continue;
35616                         }
35617                         return cfg[ sizes[i] ];
35618                 }
35619                 return 1;
35620                 
35621     },
35622
35623     /**
35624      * Sets the width for a column.
35625      * @param {Number} col The column index
35626      * @param {Number} width The new width
35627      */
35628     setColumnWidth : function(col, width, suppressEvent){
35629         this.config[col].width = width;
35630         this.totalWidth = null;
35631         if(!suppressEvent){
35632              this.fireEvent("widthchange", this, col, width);
35633         }
35634     },
35635
35636     /**
35637      * Returns the total width of all columns.
35638      * @param {Boolean} includeHidden True to include hidden column widths
35639      * @return {Number}
35640      */
35641     getTotalWidth : function(includeHidden){
35642         if(!this.totalWidth){
35643             this.totalWidth = 0;
35644             for(var i = 0, len = this.config.length; i < len; i++){
35645                 if(includeHidden || !this.isHidden(i)){
35646                     this.totalWidth += this.getColumnWidth(i);
35647                 }
35648             }
35649         }
35650         return this.totalWidth;
35651     },
35652
35653     /**
35654      * Returns the header for the specified column.
35655      * @param {Number} col The column index
35656      * @return {String}
35657      */
35658     getColumnHeader : function(col){
35659         return this.config[col].header;
35660     },
35661
35662     /**
35663      * Sets the header for a column.
35664      * @param {Number} col The column index
35665      * @param {String} header The new header
35666      */
35667     setColumnHeader : function(col, header){
35668         this.config[col].header = header;
35669         this.fireEvent("headerchange", this, col, header);
35670     },
35671
35672     /**
35673      * Returns the tooltip for the specified column.
35674      * @param {Number} col The column index
35675      * @return {String}
35676      */
35677     getColumnTooltip : function(col){
35678             return this.config[col].tooltip;
35679     },
35680     /**
35681      * Sets the tooltip for a column.
35682      * @param {Number} col The column index
35683      * @param {String} tooltip The new tooltip
35684      */
35685     setColumnTooltip : function(col, tooltip){
35686             this.config[col].tooltip = tooltip;
35687     },
35688
35689     /**
35690      * Returns the dataIndex for the specified column.
35691      * @param {Number} col The column index
35692      * @return {Number}
35693      */
35694     getDataIndex : function(col){
35695         return this.config[col].dataIndex;
35696     },
35697
35698     /**
35699      * Sets the dataIndex for a column.
35700      * @param {Number} col The column index
35701      * @param {Number} dataIndex The new dataIndex
35702      */
35703     setDataIndex : function(col, dataIndex){
35704         this.config[col].dataIndex = dataIndex;
35705     },
35706
35707     
35708     
35709     /**
35710      * Returns true if the cell is editable.
35711      * @param {Number} colIndex The column index
35712      * @param {Number} rowIndex The row index - this is nto actually used..?
35713      * @return {Boolean}
35714      */
35715     isCellEditable : function(colIndex, rowIndex){
35716         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35717     },
35718
35719     /**
35720      * Returns the editor defined for the cell/column.
35721      * return false or null to disable editing.
35722      * @param {Number} colIndex The column index
35723      * @param {Number} rowIndex The row index
35724      * @return {Object}
35725      */
35726     getCellEditor : function(colIndex, rowIndex){
35727         return this.config[colIndex].editor;
35728     },
35729
35730     /**
35731      * Sets if a column is editable.
35732      * @param {Number} col The column index
35733      * @param {Boolean} editable True if the column is editable
35734      */
35735     setEditable : function(col, editable){
35736         this.config[col].editable = editable;
35737     },
35738
35739
35740     /**
35741      * Returns true if the column is hidden.
35742      * @param {Number} colIndex The column index
35743      * @return {Boolean}
35744      */
35745     isHidden : function(colIndex){
35746         return this.config[colIndex].hidden;
35747     },
35748
35749
35750     /**
35751      * Returns true if the column width cannot be changed
35752      */
35753     isFixed : function(colIndex){
35754         return this.config[colIndex].fixed;
35755     },
35756
35757     /**
35758      * Returns true if the column can be resized
35759      * @return {Boolean}
35760      */
35761     isResizable : function(colIndex){
35762         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35763     },
35764     /**
35765      * Sets if a column is hidden.
35766      * @param {Number} colIndex The column index
35767      * @param {Boolean} hidden True if the column is hidden
35768      */
35769     setHidden : function(colIndex, hidden){
35770         this.config[colIndex].hidden = hidden;
35771         this.totalWidth = null;
35772         this.fireEvent("hiddenchange", this, colIndex, hidden);
35773     },
35774
35775     /**
35776      * Sets the editor for a column.
35777      * @param {Number} col The column index
35778      * @param {Object} editor The editor object
35779      */
35780     setEditor : function(col, editor){
35781         this.config[col].editor = editor;
35782     },
35783     /**
35784      * Add a column (experimental...) - defaults to adding to the end..
35785      * @param {Object} config 
35786     */
35787     addColumn : function(c)
35788     {
35789     
35790         var i = this.config.length;
35791         this.config[i] = c;
35792         
35793         if(typeof c.dataIndex == "undefined"){
35794             c.dataIndex = i;
35795         }
35796         if(typeof c.renderer == "string"){
35797             c.renderer = Roo.util.Format[c.renderer];
35798         }
35799         if(typeof c.id == "undefined"){
35800             c.id = Roo.id();
35801         }
35802         if(c.editor && c.editor.xtype){
35803             c.editor  = Roo.factory(c.editor, Roo.grid);
35804         }
35805         if(c.editor && c.editor.isFormField){
35806             c.editor = new Roo.grid.GridEditor(c.editor);
35807         }
35808         this.lookup[c.id] = c;
35809     }
35810     
35811 });
35812
35813 Roo.grid.ColumnModel.defaultRenderer = function(value)
35814 {
35815     if(typeof value == "object") {
35816         return value;
35817     }
35818         if(typeof value == "string" && value.length < 1){
35819             return "&#160;";
35820         }
35821     
35822         return String.format("{0}", value);
35823 };
35824
35825 // Alias for backwards compatibility
35826 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35827 /*
35828  * Based on:
35829  * Ext JS Library 1.1.1
35830  * Copyright(c) 2006-2007, Ext JS, LLC.
35831  *
35832  * Originally Released Under LGPL - original licence link has changed is not relivant.
35833  *
35834  * Fork - LGPL
35835  * <script type="text/javascript">
35836  */
35837
35838 /**
35839  * @class Roo.grid.AbstractSelectionModel
35840  * @extends Roo.util.Observable
35841  * @abstract
35842  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35843  * implemented by descendant classes.  This class should not be directly instantiated.
35844  * @constructor
35845  */
35846 Roo.grid.AbstractSelectionModel = function(){
35847     this.locked = false;
35848     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35849 };
35850
35851 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35852     /** @ignore Called by the grid automatically. Do not call directly. */
35853     init : function(grid){
35854         this.grid = grid;
35855         this.initEvents();
35856     },
35857
35858     /**
35859      * Locks the selections.
35860      */
35861     lock : function(){
35862         this.locked = true;
35863     },
35864
35865     /**
35866      * Unlocks the selections.
35867      */
35868     unlock : function(){
35869         this.locked = false;
35870     },
35871
35872     /**
35873      * Returns true if the selections are locked.
35874      * @return {Boolean}
35875      */
35876     isLocked : function(){
35877         return this.locked;
35878     }
35879 });/*
35880  * Based on:
35881  * Ext JS Library 1.1.1
35882  * Copyright(c) 2006-2007, Ext JS, LLC.
35883  *
35884  * Originally Released Under LGPL - original licence link has changed is not relivant.
35885  *
35886  * Fork - LGPL
35887  * <script type="text/javascript">
35888  */
35889 /**
35890  * @extends Roo.grid.AbstractSelectionModel
35891  * @class Roo.grid.RowSelectionModel
35892  * The default SelectionModel used by {@link Roo.grid.Grid}.
35893  * It supports multiple selections and keyboard selection/navigation. 
35894  * @constructor
35895  * @param {Object} config
35896  */
35897 Roo.grid.RowSelectionModel = function(config){
35898     Roo.apply(this, config);
35899     this.selections = new Roo.util.MixedCollection(false, function(o){
35900         return o.id;
35901     });
35902
35903     this.last = false;
35904     this.lastActive = false;
35905
35906     this.addEvents({
35907         /**
35908         * @event selectionchange
35909         * Fires when the selection changes
35910         * @param {SelectionModel} this
35911         */
35912        "selectionchange" : true,
35913        /**
35914         * @event afterselectionchange
35915         * Fires after the selection changes (eg. by key press or clicking)
35916         * @param {SelectionModel} this
35917         */
35918        "afterselectionchange" : true,
35919        /**
35920         * @event beforerowselect
35921         * Fires when a row is selected being selected, return false to cancel.
35922         * @param {SelectionModel} this
35923         * @param {Number} rowIndex The selected index
35924         * @param {Boolean} keepExisting False if other selections will be cleared
35925         */
35926        "beforerowselect" : true,
35927        /**
35928         * @event rowselect
35929         * Fires when a row is selected.
35930         * @param {SelectionModel} this
35931         * @param {Number} rowIndex The selected index
35932         * @param {Roo.data.Record} r The record
35933         */
35934        "rowselect" : true,
35935        /**
35936         * @event rowdeselect
35937         * Fires when a row is deselected.
35938         * @param {SelectionModel} this
35939         * @param {Number} rowIndex The selected index
35940         */
35941         "rowdeselect" : true
35942     });
35943     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35944     this.locked = false;
35945 };
35946
35947 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35948     /**
35949      * @cfg {Boolean} singleSelect
35950      * True to allow selection of only one row at a time (defaults to false)
35951      */
35952     singleSelect : false,
35953
35954     // private
35955     initEvents : function(){
35956
35957         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35958             this.grid.on("mousedown", this.handleMouseDown, this);
35959         }else{ // allow click to work like normal
35960             this.grid.on("rowclick", this.handleDragableRowClick, this);
35961         }
35962         // bootstrap does not have a view..
35963         var view = this.grid.view ? this.grid.view : this.grid;
35964         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35965             "up" : function(e){
35966                 if(!e.shiftKey){
35967                     this.selectPrevious(e.shiftKey);
35968                 }else if(this.last !== false && this.lastActive !== false){
35969                     var last = this.last;
35970                     this.selectRange(this.last,  this.lastActive-1);
35971                     view.focusRow(this.lastActive);
35972                     if(last !== false){
35973                         this.last = last;
35974                     }
35975                 }else{
35976                     this.selectFirstRow();
35977                 }
35978                 this.fireEvent("afterselectionchange", this);
35979             },
35980             "down" : function(e){
35981                 if(!e.shiftKey){
35982                     this.selectNext(e.shiftKey);
35983                 }else if(this.last !== false && this.lastActive !== false){
35984                     var last = this.last;
35985                     this.selectRange(this.last,  this.lastActive+1);
35986                     view.focusRow(this.lastActive);
35987                     if(last !== false){
35988                         this.last = last;
35989                     }
35990                 }else{
35991                     this.selectFirstRow();
35992                 }
35993                 this.fireEvent("afterselectionchange", this);
35994             },
35995             scope: this
35996         });
35997
35998          
35999         view.on("refresh", this.onRefresh, this);
36000         view.on("rowupdated", this.onRowUpdated, this);
36001         view.on("rowremoved", this.onRemove, this);
36002     },
36003
36004     // private
36005     onRefresh : function(){
36006         var ds = this.grid.ds, i, v = this.grid.view;
36007         var s = this.selections;
36008         s.each(function(r){
36009             if((i = ds.indexOfId(r.id)) != -1){
36010                 v.onRowSelect(i);
36011                 s.add(ds.getAt(i)); // updating the selection relate data
36012             }else{
36013                 s.remove(r);
36014             }
36015         });
36016     },
36017
36018     // private
36019     onRemove : function(v, index, r){
36020         this.selections.remove(r);
36021     },
36022
36023     // private
36024     onRowUpdated : function(v, index, r){
36025         if(this.isSelected(r)){
36026             v.onRowSelect(index);
36027         }
36028     },
36029
36030     /**
36031      * Select records.
36032      * @param {Array} records The records to select
36033      * @param {Boolean} keepExisting (optional) True to keep existing selections
36034      */
36035     selectRecords : function(records, keepExisting){
36036         if(!keepExisting){
36037             this.clearSelections();
36038         }
36039         var ds = this.grid.ds;
36040         for(var i = 0, len = records.length; i < len; i++){
36041             this.selectRow(ds.indexOf(records[i]), true);
36042         }
36043     },
36044
36045     /**
36046      * Gets the number of selected rows.
36047      * @return {Number}
36048      */
36049     getCount : function(){
36050         return this.selections.length;
36051     },
36052
36053     /**
36054      * Selects the first row in the grid.
36055      */
36056     selectFirstRow : function(){
36057         this.selectRow(0);
36058     },
36059
36060     /**
36061      * Select the last row.
36062      * @param {Boolean} keepExisting (optional) True to keep existing selections
36063      */
36064     selectLastRow : function(keepExisting){
36065         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
36066     },
36067
36068     /**
36069      * Selects the row immediately following the last selected row.
36070      * @param {Boolean} keepExisting (optional) True to keep existing selections
36071      */
36072     selectNext : function(keepExisting){
36073         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
36074             this.selectRow(this.last+1, keepExisting);
36075             var view = this.grid.view ? this.grid.view : this.grid;
36076             view.focusRow(this.last);
36077         }
36078     },
36079
36080     /**
36081      * Selects the row that precedes the last selected row.
36082      * @param {Boolean} keepExisting (optional) True to keep existing selections
36083      */
36084     selectPrevious : function(keepExisting){
36085         if(this.last){
36086             this.selectRow(this.last-1, keepExisting);
36087             var view = this.grid.view ? this.grid.view : this.grid;
36088             view.focusRow(this.last);
36089         }
36090     },
36091
36092     /**
36093      * Returns the selected records
36094      * @return {Array} Array of selected records
36095      */
36096     getSelections : function(){
36097         return [].concat(this.selections.items);
36098     },
36099
36100     /**
36101      * Returns the first selected record.
36102      * @return {Record}
36103      */
36104     getSelected : function(){
36105         return this.selections.itemAt(0);
36106     },
36107
36108
36109     /**
36110      * Clears all selections.
36111      */
36112     clearSelections : function(fast){
36113         if(this.locked) {
36114             return;
36115         }
36116         if(fast !== true){
36117             var ds = this.grid.ds;
36118             var s = this.selections;
36119             s.each(function(r){
36120                 this.deselectRow(ds.indexOfId(r.id));
36121             }, this);
36122             s.clear();
36123         }else{
36124             this.selections.clear();
36125         }
36126         this.last = false;
36127     },
36128
36129
36130     /**
36131      * Selects all rows.
36132      */
36133     selectAll : function(){
36134         if(this.locked) {
36135             return;
36136         }
36137         this.selections.clear();
36138         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
36139             this.selectRow(i, true);
36140         }
36141     },
36142
36143     /**
36144      * Returns True if there is a selection.
36145      * @return {Boolean}
36146      */
36147     hasSelection : function(){
36148         return this.selections.length > 0;
36149     },
36150
36151     /**
36152      * Returns True if the specified row is selected.
36153      * @param {Number/Record} record The record or index of the record to check
36154      * @return {Boolean}
36155      */
36156     isSelected : function(index){
36157         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
36158         return (r && this.selections.key(r.id) ? true : false);
36159     },
36160
36161     /**
36162      * Returns True if the specified record id is selected.
36163      * @param {String} id The id of record to check
36164      * @return {Boolean}
36165      */
36166     isIdSelected : function(id){
36167         return (this.selections.key(id) ? true : false);
36168     },
36169
36170     // private
36171     handleMouseDown : function(e, t)
36172     {
36173         var view = this.grid.view ? this.grid.view : this.grid;
36174         var rowIndex;
36175         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
36176             return;
36177         };
36178         if(e.shiftKey && this.last !== false){
36179             var last = this.last;
36180             this.selectRange(last, rowIndex, e.ctrlKey);
36181             this.last = last; // reset the last
36182             view.focusRow(rowIndex);
36183         }else{
36184             var isSelected = this.isSelected(rowIndex);
36185             if(e.button !== 0 && isSelected){
36186                 view.focusRow(rowIndex);
36187             }else if(e.ctrlKey && isSelected){
36188                 this.deselectRow(rowIndex);
36189             }else if(!isSelected){
36190                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
36191                 view.focusRow(rowIndex);
36192             }
36193         }
36194         this.fireEvent("afterselectionchange", this);
36195     },
36196     // private
36197     handleDragableRowClick :  function(grid, rowIndex, e) 
36198     {
36199         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
36200             this.selectRow(rowIndex, false);
36201             var view = this.grid.view ? this.grid.view : this.grid;
36202             view.focusRow(rowIndex);
36203              this.fireEvent("afterselectionchange", this);
36204         }
36205     },
36206     
36207     /**
36208      * Selects multiple rows.
36209      * @param {Array} rows Array of the indexes of the row to select
36210      * @param {Boolean} keepExisting (optional) True to keep existing selections
36211      */
36212     selectRows : function(rows, keepExisting){
36213         if(!keepExisting){
36214             this.clearSelections();
36215         }
36216         for(var i = 0, len = rows.length; i < len; i++){
36217             this.selectRow(rows[i], true);
36218         }
36219     },
36220
36221     /**
36222      * Selects a range of rows. All rows in between startRow and endRow are also selected.
36223      * @param {Number} startRow The index of the first row in the range
36224      * @param {Number} endRow The index of the last row in the range
36225      * @param {Boolean} keepExisting (optional) True to retain existing selections
36226      */
36227     selectRange : function(startRow, endRow, keepExisting){
36228         if(this.locked) {
36229             return;
36230         }
36231         if(!keepExisting){
36232             this.clearSelections();
36233         }
36234         if(startRow <= endRow){
36235             for(var i = startRow; i <= endRow; i++){
36236                 this.selectRow(i, true);
36237             }
36238         }else{
36239             for(var i = startRow; i >= endRow; i--){
36240                 this.selectRow(i, true);
36241             }
36242         }
36243     },
36244
36245     /**
36246      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
36247      * @param {Number} startRow The index of the first row in the range
36248      * @param {Number} endRow The index of the last row in the range
36249      */
36250     deselectRange : function(startRow, endRow, preventViewNotify){
36251         if(this.locked) {
36252             return;
36253         }
36254         for(var i = startRow; i <= endRow; i++){
36255             this.deselectRow(i, preventViewNotify);
36256         }
36257     },
36258
36259     /**
36260      * Selects a row.
36261      * @param {Number} row The index of the row to select
36262      * @param {Boolean} keepExisting (optional) True to keep existing selections
36263      */
36264     selectRow : function(index, keepExisting, preventViewNotify){
36265         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
36266             return;
36267         }
36268         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36269             if(!keepExisting || this.singleSelect){
36270                 this.clearSelections();
36271             }
36272             var r = this.grid.ds.getAt(index);
36273             this.selections.add(r);
36274             this.last = this.lastActive = index;
36275             if(!preventViewNotify){
36276                 var view = this.grid.view ? this.grid.view : this.grid;
36277                 view.onRowSelect(index);
36278             }
36279             this.fireEvent("rowselect", this, index, r);
36280             this.fireEvent("selectionchange", this);
36281         }
36282     },
36283
36284     /**
36285      * Deselects a row.
36286      * @param {Number} row The index of the row to deselect
36287      */
36288     deselectRow : function(index, preventViewNotify){
36289         if(this.locked) {
36290             return;
36291         }
36292         if(this.last == index){
36293             this.last = false;
36294         }
36295         if(this.lastActive == index){
36296             this.lastActive = false;
36297         }
36298         var r = this.grid.ds.getAt(index);
36299         this.selections.remove(r);
36300         if(!preventViewNotify){
36301             var view = this.grid.view ? this.grid.view : this.grid;
36302             view.onRowDeselect(index);
36303         }
36304         this.fireEvent("rowdeselect", this, index);
36305         this.fireEvent("selectionchange", this);
36306     },
36307
36308     // private
36309     restoreLast : function(){
36310         if(this._last){
36311             this.last = this._last;
36312         }
36313     },
36314
36315     // private
36316     acceptsNav : function(row, col, cm){
36317         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36318     },
36319
36320     // private
36321     onEditorKey : function(field, e){
36322         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36323         if(k == e.TAB){
36324             e.stopEvent();
36325             ed.completeEdit();
36326             if(e.shiftKey){
36327                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36328             }else{
36329                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36330             }
36331         }else if(k == e.ENTER && !e.ctrlKey){
36332             e.stopEvent();
36333             ed.completeEdit();
36334             if(e.shiftKey){
36335                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36336             }else{
36337                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36338             }
36339         }else if(k == e.ESC){
36340             ed.cancelEdit();
36341         }
36342         if(newCell){
36343             g.startEditing(newCell[0], newCell[1]);
36344         }
36345     }
36346 });/*
36347  * Based on:
36348  * Ext JS Library 1.1.1
36349  * Copyright(c) 2006-2007, Ext JS, LLC.
36350  *
36351  * Originally Released Under LGPL - original licence link has changed is not relivant.
36352  *
36353  * Fork - LGPL
36354  * <script type="text/javascript">
36355  */
36356 /**
36357  * @class Roo.grid.CellSelectionModel
36358  * @extends Roo.grid.AbstractSelectionModel
36359  * This class provides the basic implementation for cell selection in a grid.
36360  * @constructor
36361  * @param {Object} config The object containing the configuration of this model.
36362  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36363  */
36364 Roo.grid.CellSelectionModel = function(config){
36365     Roo.apply(this, config);
36366
36367     this.selection = null;
36368
36369     this.addEvents({
36370         /**
36371              * @event beforerowselect
36372              * Fires before a cell is selected.
36373              * @param {SelectionModel} this
36374              * @param {Number} rowIndex The selected row index
36375              * @param {Number} colIndex The selected cell index
36376              */
36377             "beforecellselect" : true,
36378         /**
36379              * @event cellselect
36380              * Fires when a cell is selected.
36381              * @param {SelectionModel} this
36382              * @param {Number} rowIndex The selected row index
36383              * @param {Number} colIndex The selected cell index
36384              */
36385             "cellselect" : true,
36386         /**
36387              * @event selectionchange
36388              * Fires when the active selection changes.
36389              * @param {SelectionModel} this
36390              * @param {Object} selection null for no selection or an object (o) with two properties
36391                 <ul>
36392                 <li>o.record: the record object for the row the selection is in</li>
36393                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36394                 </ul>
36395              */
36396             "selectionchange" : true,
36397         /**
36398              * @event tabend
36399              * Fires when the tab (or enter) was pressed on the last editable cell
36400              * You can use this to trigger add new row.
36401              * @param {SelectionModel} this
36402              */
36403             "tabend" : true,
36404          /**
36405              * @event beforeeditnext
36406              * Fires before the next editable sell is made active
36407              * You can use this to skip to another cell or fire the tabend
36408              *    if you set cell to false
36409              * @param {Object} eventdata object : { cell : [ row, col ] } 
36410              */
36411             "beforeeditnext" : true
36412     });
36413     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36414 };
36415
36416 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
36417     
36418     enter_is_tab: false,
36419
36420     /** @ignore */
36421     initEvents : function(){
36422         this.grid.on("mousedown", this.handleMouseDown, this);
36423         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36424         var view = this.grid.view;
36425         view.on("refresh", this.onViewChange, this);
36426         view.on("rowupdated", this.onRowUpdated, this);
36427         view.on("beforerowremoved", this.clearSelections, this);
36428         view.on("beforerowsinserted", this.clearSelections, this);
36429         if(this.grid.isEditor){
36430             this.grid.on("beforeedit", this.beforeEdit,  this);
36431         }
36432     },
36433
36434         //private
36435     beforeEdit : function(e){
36436         this.select(e.row, e.column, false, true, e.record);
36437     },
36438
36439         //private
36440     onRowUpdated : function(v, index, r){
36441         if(this.selection && this.selection.record == r){
36442             v.onCellSelect(index, this.selection.cell[1]);
36443         }
36444     },
36445
36446         //private
36447     onViewChange : function(){
36448         this.clearSelections(true);
36449     },
36450
36451         /**
36452          * Returns the currently selected cell,.
36453          * @return {Array} The selected cell (row, column) or null if none selected.
36454          */
36455     getSelectedCell : function(){
36456         return this.selection ? this.selection.cell : null;
36457     },
36458
36459     /**
36460      * Clears all selections.
36461      * @param {Boolean} true to prevent the gridview from being notified about the change.
36462      */
36463     clearSelections : function(preventNotify){
36464         var s = this.selection;
36465         if(s){
36466             if(preventNotify !== true){
36467                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36468             }
36469             this.selection = null;
36470             this.fireEvent("selectionchange", this, null);
36471         }
36472     },
36473
36474     /**
36475      * Returns true if there is a selection.
36476      * @return {Boolean}
36477      */
36478     hasSelection : function(){
36479         return this.selection ? true : false;
36480     },
36481
36482     /** @ignore */
36483     handleMouseDown : function(e, t){
36484         var v = this.grid.getView();
36485         if(this.isLocked()){
36486             return;
36487         };
36488         var row = v.findRowIndex(t);
36489         var cell = v.findCellIndex(t);
36490         if(row !== false && cell !== false){
36491             this.select(row, cell);
36492         }
36493     },
36494
36495     /**
36496      * Selects a cell.
36497      * @param {Number} rowIndex
36498      * @param {Number} collIndex
36499      */
36500     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36501         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36502             this.clearSelections();
36503             r = r || this.grid.dataSource.getAt(rowIndex);
36504             this.selection = {
36505                 record : r,
36506                 cell : [rowIndex, colIndex]
36507             };
36508             if(!preventViewNotify){
36509                 var v = this.grid.getView();
36510                 v.onCellSelect(rowIndex, colIndex);
36511                 if(preventFocus !== true){
36512                     v.focusCell(rowIndex, colIndex);
36513                 }
36514             }
36515             this.fireEvent("cellselect", this, rowIndex, colIndex);
36516             this.fireEvent("selectionchange", this, this.selection);
36517         }
36518     },
36519
36520         //private
36521     isSelectable : function(rowIndex, colIndex, cm){
36522         return !cm.isHidden(colIndex);
36523     },
36524
36525     /** @ignore */
36526     handleKeyDown : function(e){
36527         //Roo.log('Cell Sel Model handleKeyDown');
36528         if(!e.isNavKeyPress()){
36529             return;
36530         }
36531         var g = this.grid, s = this.selection;
36532         if(!s){
36533             e.stopEvent();
36534             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
36535             if(cell){
36536                 this.select(cell[0], cell[1]);
36537             }
36538             return;
36539         }
36540         var sm = this;
36541         var walk = function(row, col, step){
36542             return g.walkCells(row, col, step, sm.isSelectable,  sm);
36543         };
36544         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36545         var newCell;
36546
36547       
36548
36549         switch(k){
36550             case e.TAB:
36551                 // handled by onEditorKey
36552                 if (g.isEditor && g.editing) {
36553                     return;
36554                 }
36555                 if(e.shiftKey) {
36556                     newCell = walk(r, c-1, -1);
36557                 } else {
36558                     newCell = walk(r, c+1, 1);
36559                 }
36560                 break;
36561             
36562             case e.DOWN:
36563                newCell = walk(r+1, c, 1);
36564                 break;
36565             
36566             case e.UP:
36567                 newCell = walk(r-1, c, -1);
36568                 break;
36569             
36570             case e.RIGHT:
36571                 newCell = walk(r, c+1, 1);
36572                 break;
36573             
36574             case e.LEFT:
36575                 newCell = walk(r, c-1, -1);
36576                 break;
36577             
36578             case e.ENTER:
36579                 
36580                 if(g.isEditor && !g.editing){
36581                    g.startEditing(r, c);
36582                    e.stopEvent();
36583                    return;
36584                 }
36585                 
36586                 
36587              break;
36588         };
36589         if(newCell){
36590             this.select(newCell[0], newCell[1]);
36591             e.stopEvent();
36592             
36593         }
36594     },
36595
36596     acceptsNav : function(row, col, cm){
36597         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36598     },
36599     /**
36600      * Selects a cell.
36601      * @param {Number} field (not used) - as it's normally used as a listener
36602      * @param {Number} e - event - fake it by using
36603      *
36604      * var e = Roo.EventObjectImpl.prototype;
36605      * e.keyCode = e.TAB
36606      *
36607      * 
36608      */
36609     onEditorKey : function(field, e){
36610         
36611         var k = e.getKey(),
36612             newCell,
36613             g = this.grid,
36614             ed = g.activeEditor,
36615             forward = false;
36616         ///Roo.log('onEditorKey' + k);
36617         
36618         
36619         if (this.enter_is_tab && k == e.ENTER) {
36620             k = e.TAB;
36621         }
36622         
36623         if(k == e.TAB){
36624             if(e.shiftKey){
36625                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36626             }else{
36627                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36628                 forward = true;
36629             }
36630             
36631             e.stopEvent();
36632             
36633         } else if(k == e.ENTER &&  !e.ctrlKey){
36634             ed.completeEdit();
36635             e.stopEvent();
36636             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36637         
36638                 } else if(k == e.ESC){
36639             ed.cancelEdit();
36640         }
36641                 
36642         if (newCell) {
36643             var ecall = { cell : newCell, forward : forward };
36644             this.fireEvent('beforeeditnext', ecall );
36645             newCell = ecall.cell;
36646                         forward = ecall.forward;
36647         }
36648                 
36649         if(newCell){
36650             //Roo.log('next cell after edit');
36651             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36652         } else if (forward) {
36653             // tabbed past last
36654             this.fireEvent.defer(100, this, ['tabend',this]);
36655         }
36656     }
36657 });/*
36658  * Based on:
36659  * Ext JS Library 1.1.1
36660  * Copyright(c) 2006-2007, Ext JS, LLC.
36661  *
36662  * Originally Released Under LGPL - original licence link has changed is not relivant.
36663  *
36664  * Fork - LGPL
36665  * <script type="text/javascript">
36666  */
36667  
36668 /**
36669  * @class Roo.grid.EditorGrid
36670  * @extends Roo.grid.Grid
36671  * Class for creating and editable grid.
36672  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36673  * The container MUST have some type of size defined for the grid to fill. The container will be 
36674  * automatically set to position relative if it isn't already.
36675  * @param {Object} dataSource The data model to bind to
36676  * @param {Object} colModel The column model with info about this grid's columns
36677  */
36678 Roo.grid.EditorGrid = function(container, config){
36679     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36680     this.getGridEl().addClass("xedit-grid");
36681
36682     if(!this.selModel){
36683         this.selModel = new Roo.grid.CellSelectionModel();
36684     }
36685
36686     this.activeEditor = null;
36687
36688         this.addEvents({
36689             /**
36690              * @event beforeedit
36691              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36692              * <ul style="padding:5px;padding-left:16px;">
36693              * <li>grid - This grid</li>
36694              * <li>record - The record being edited</li>
36695              * <li>field - The field name being edited</li>
36696              * <li>value - The value for the field being edited.</li>
36697              * <li>row - The grid row index</li>
36698              * <li>column - The grid column index</li>
36699              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36700              * </ul>
36701              * @param {Object} e An edit event (see above for description)
36702              */
36703             "beforeedit" : true,
36704             /**
36705              * @event afteredit
36706              * Fires after a cell is edited. <br />
36707              * <ul style="padding:5px;padding-left:16px;">
36708              * <li>grid - This grid</li>
36709              * <li>record - The record being edited</li>
36710              * <li>field - The field name being edited</li>
36711              * <li>value - The value being set</li>
36712              * <li>originalValue - The original value for the field, before the edit.</li>
36713              * <li>row - The grid row index</li>
36714              * <li>column - The grid column index</li>
36715              * </ul>
36716              * @param {Object} e An edit event (see above for description)
36717              */
36718             "afteredit" : true,
36719             /**
36720              * @event validateedit
36721              * Fires after a cell is edited, but before the value is set in the record. 
36722          * You can use this to modify the value being set in the field, Return false
36723              * to cancel the change. The edit event object has the following properties <br />
36724              * <ul style="padding:5px;padding-left:16px;">
36725          * <li>editor - This editor</li>
36726              * <li>grid - This grid</li>
36727              * <li>record - The record being edited</li>
36728              * <li>field - The field name being edited</li>
36729              * <li>value - The value being set</li>
36730              * <li>originalValue - The original value for the field, before the edit.</li>
36731              * <li>row - The grid row index</li>
36732              * <li>column - The grid column index</li>
36733              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36734              * </ul>
36735              * @param {Object} e An edit event (see above for description)
36736              */
36737             "validateedit" : true
36738         });
36739     this.on("bodyscroll", this.stopEditing,  this);
36740     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36741 };
36742
36743 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36744     /**
36745      * @cfg {Number} clicksToEdit
36746      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36747      */
36748     clicksToEdit: 2,
36749
36750     // private
36751     isEditor : true,
36752     // private
36753     trackMouseOver: false, // causes very odd FF errors
36754
36755     onCellDblClick : function(g, row, col){
36756         this.startEditing(row, col);
36757     },
36758
36759     onEditComplete : function(ed, value, startValue){
36760         this.editing = false;
36761         this.activeEditor = null;
36762         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36763         var r = ed.record;
36764         var field = this.colModel.getDataIndex(ed.col);
36765         var e = {
36766             grid: this,
36767             record: r,
36768             field: field,
36769             originalValue: startValue,
36770             value: value,
36771             row: ed.row,
36772             column: ed.col,
36773             cancel:false,
36774             editor: ed
36775         };
36776         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36777         cell.show();
36778           
36779         if(String(value) !== String(startValue)){
36780             
36781             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36782                 r.set(field, e.value);
36783                 // if we are dealing with a combo box..
36784                 // then we also set the 'name' colum to be the displayField
36785                 if (ed.field.displayField && ed.field.name) {
36786                     r.set(ed.field.name, ed.field.el.dom.value);
36787                 }
36788                 
36789                 delete e.cancel; //?? why!!!
36790                 this.fireEvent("afteredit", e);
36791             }
36792         } else {
36793             this.fireEvent("afteredit", e); // always fire it!
36794         }
36795         this.view.focusCell(ed.row, ed.col);
36796     },
36797
36798     /**
36799      * Starts editing the specified for the specified row/column
36800      * @param {Number} rowIndex
36801      * @param {Number} colIndex
36802      */
36803     startEditing : function(row, col){
36804         this.stopEditing();
36805         if(this.colModel.isCellEditable(col, row)){
36806             this.view.ensureVisible(row, col, true);
36807           
36808             var r = this.dataSource.getAt(row);
36809             var field = this.colModel.getDataIndex(col);
36810             var cell = Roo.get(this.view.getCell(row,col));
36811             var e = {
36812                 grid: this,
36813                 record: r,
36814                 field: field,
36815                 value: r.data[field],
36816                 row: row,
36817                 column: col,
36818                 cancel:false 
36819             };
36820             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36821                 this.editing = true;
36822                 var ed = this.colModel.getCellEditor(col, row);
36823                 
36824                 if (!ed) {
36825                     return;
36826                 }
36827                 if(!ed.rendered){
36828                     ed.render(ed.parentEl || document.body);
36829                 }
36830                 ed.field.reset();
36831                
36832                 cell.hide();
36833                 
36834                 (function(){ // complex but required for focus issues in safari, ie and opera
36835                     ed.row = row;
36836                     ed.col = col;
36837                     ed.record = r;
36838                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
36839                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
36840                     this.activeEditor = ed;
36841                     var v = r.data[field];
36842                     ed.startEdit(this.view.getCell(row, col), v);
36843                     // combo's with 'displayField and name set
36844                     if (ed.field.displayField && ed.field.name) {
36845                         ed.field.el.dom.value = r.data[ed.field.name];
36846                     }
36847                     
36848                     
36849                 }).defer(50, this);
36850             }
36851         }
36852     },
36853         
36854     /**
36855      * Stops any active editing
36856      */
36857     stopEditing : function(){
36858         if(this.activeEditor){
36859             this.activeEditor.completeEdit();
36860         }
36861         this.activeEditor = null;
36862     },
36863         
36864          /**
36865      * Called to get grid's drag proxy text, by default returns this.ddText.
36866      * @return {String}
36867      */
36868     getDragDropText : function(){
36869         var count = this.selModel.getSelectedCell() ? 1 : 0;
36870         return String.format(this.ddText, count, count == 1 ? '' : 's');
36871     }
36872         
36873 });/*
36874  * Based on:
36875  * Ext JS Library 1.1.1
36876  * Copyright(c) 2006-2007, Ext JS, LLC.
36877  *
36878  * Originally Released Under LGPL - original licence link has changed is not relivant.
36879  *
36880  * Fork - LGPL
36881  * <script type="text/javascript">
36882  */
36883
36884 // private - not really -- you end up using it !
36885 // This is a support class used internally by the Grid components
36886
36887 /**
36888  * @class Roo.grid.GridEditor
36889  * @extends Roo.Editor
36890  * Class for creating and editable grid elements.
36891  * @param {Object} config any settings (must include field)
36892  */
36893 Roo.grid.GridEditor = function(field, config){
36894     if (!config && field.field) {
36895         config = field;
36896         field = Roo.factory(config.field, Roo.form);
36897     }
36898     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36899     field.monitorTab = false;
36900 };
36901
36902 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36903     
36904     /**
36905      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36906      */
36907     
36908     alignment: "tl-tl",
36909     autoSize: "width",
36910     hideEl : false,
36911     cls: "x-small-editor x-grid-editor",
36912     shim:false,
36913     shadow:"frame"
36914 });/*
36915  * Based on:
36916  * Ext JS Library 1.1.1
36917  * Copyright(c) 2006-2007, Ext JS, LLC.
36918  *
36919  * Originally Released Under LGPL - original licence link has changed is not relivant.
36920  *
36921  * Fork - LGPL
36922  * <script type="text/javascript">
36923  */
36924   
36925
36926   
36927 Roo.grid.PropertyRecord = Roo.data.Record.create([
36928     {name:'name',type:'string'},  'value'
36929 ]);
36930
36931
36932 Roo.grid.PropertyStore = function(grid, source){
36933     this.grid = grid;
36934     this.store = new Roo.data.Store({
36935         recordType : Roo.grid.PropertyRecord
36936     });
36937     this.store.on('update', this.onUpdate,  this);
36938     if(source){
36939         this.setSource(source);
36940     }
36941     Roo.grid.PropertyStore.superclass.constructor.call(this);
36942 };
36943
36944
36945
36946 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36947     setSource : function(o){
36948         this.source = o;
36949         this.store.removeAll();
36950         var data = [];
36951         for(var k in o){
36952             if(this.isEditableValue(o[k])){
36953                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36954             }
36955         }
36956         this.store.loadRecords({records: data}, {}, true);
36957     },
36958
36959     onUpdate : function(ds, record, type){
36960         if(type == Roo.data.Record.EDIT){
36961             var v = record.data['value'];
36962             var oldValue = record.modified['value'];
36963             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36964                 this.source[record.id] = v;
36965                 record.commit();
36966                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36967             }else{
36968                 record.reject();
36969             }
36970         }
36971     },
36972
36973     getProperty : function(row){
36974        return this.store.getAt(row);
36975     },
36976
36977     isEditableValue: function(val){
36978         if(val && val instanceof Date){
36979             return true;
36980         }else if(typeof val == 'object' || typeof val == 'function'){
36981             return false;
36982         }
36983         return true;
36984     },
36985
36986     setValue : function(prop, value){
36987         this.source[prop] = value;
36988         this.store.getById(prop).set('value', value);
36989     },
36990
36991     getSource : function(){
36992         return this.source;
36993     }
36994 });
36995
36996 Roo.grid.PropertyColumnModel = function(grid, store){
36997     this.grid = grid;
36998     var g = Roo.grid;
36999     g.PropertyColumnModel.superclass.constructor.call(this, [
37000         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
37001         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
37002     ]);
37003     this.store = store;
37004     this.bselect = Roo.DomHelper.append(document.body, {
37005         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
37006             {tag: 'option', value: 'true', html: 'true'},
37007             {tag: 'option', value: 'false', html: 'false'}
37008         ]
37009     });
37010     Roo.id(this.bselect);
37011     var f = Roo.form;
37012     this.editors = {
37013         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
37014         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
37015         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
37016         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
37017         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
37018     };
37019     this.renderCellDelegate = this.renderCell.createDelegate(this);
37020     this.renderPropDelegate = this.renderProp.createDelegate(this);
37021 };
37022
37023 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
37024     
37025     
37026     nameText : 'Name',
37027     valueText : 'Value',
37028     
37029     dateFormat : 'm/j/Y',
37030     
37031     
37032     renderDate : function(dateVal){
37033         return dateVal.dateFormat(this.dateFormat);
37034     },
37035
37036     renderBool : function(bVal){
37037         return bVal ? 'true' : 'false';
37038     },
37039
37040     isCellEditable : function(colIndex, rowIndex){
37041         return colIndex == 1;
37042     },
37043
37044     getRenderer : function(col){
37045         return col == 1 ?
37046             this.renderCellDelegate : this.renderPropDelegate;
37047     },
37048
37049     renderProp : function(v){
37050         return this.getPropertyName(v);
37051     },
37052
37053     renderCell : function(val){
37054         var rv = val;
37055         if(val instanceof Date){
37056             rv = this.renderDate(val);
37057         }else if(typeof val == 'boolean'){
37058             rv = this.renderBool(val);
37059         }
37060         return Roo.util.Format.htmlEncode(rv);
37061     },
37062
37063     getPropertyName : function(name){
37064         var pn = this.grid.propertyNames;
37065         return pn && pn[name] ? pn[name] : name;
37066     },
37067
37068     getCellEditor : function(colIndex, rowIndex){
37069         var p = this.store.getProperty(rowIndex);
37070         var n = p.data['name'], val = p.data['value'];
37071         
37072         if(typeof(this.grid.customEditors[n]) == 'string'){
37073             return this.editors[this.grid.customEditors[n]];
37074         }
37075         if(typeof(this.grid.customEditors[n]) != 'undefined'){
37076             return this.grid.customEditors[n];
37077         }
37078         if(val instanceof Date){
37079             return this.editors['date'];
37080         }else if(typeof val == 'number'){
37081             return this.editors['number'];
37082         }else if(typeof val == 'boolean'){
37083             return this.editors['boolean'];
37084         }else{
37085             return this.editors['string'];
37086         }
37087     }
37088 });
37089
37090 /**
37091  * @class Roo.grid.PropertyGrid
37092  * @extends Roo.grid.EditorGrid
37093  * This class represents the  interface of a component based property grid control.
37094  * <br><br>Usage:<pre><code>
37095  var grid = new Roo.grid.PropertyGrid("my-container-id", {
37096       
37097  });
37098  // set any options
37099  grid.render();
37100  * </code></pre>
37101   
37102  * @constructor
37103  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37104  * The container MUST have some type of size defined for the grid to fill. The container will be
37105  * automatically set to position relative if it isn't already.
37106  * @param {Object} config A config object that sets properties on this grid.
37107  */
37108 Roo.grid.PropertyGrid = function(container, config){
37109     config = config || {};
37110     var store = new Roo.grid.PropertyStore(this);
37111     this.store = store;
37112     var cm = new Roo.grid.PropertyColumnModel(this, store);
37113     store.store.sort('name', 'ASC');
37114     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
37115         ds: store.store,
37116         cm: cm,
37117         enableColLock:false,
37118         enableColumnMove:false,
37119         stripeRows:false,
37120         trackMouseOver: false,
37121         clicksToEdit:1
37122     }, config));
37123     this.getGridEl().addClass('x-props-grid');
37124     this.lastEditRow = null;
37125     this.on('columnresize', this.onColumnResize, this);
37126     this.addEvents({
37127          /**
37128              * @event beforepropertychange
37129              * Fires before a property changes (return false to stop?)
37130              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37131              * @param {String} id Record Id
37132              * @param {String} newval New Value
37133          * @param {String} oldval Old Value
37134              */
37135         "beforepropertychange": true,
37136         /**
37137              * @event propertychange
37138              * Fires after a property changes
37139              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37140              * @param {String} id Record Id
37141              * @param {String} newval New Value
37142          * @param {String} oldval Old Value
37143              */
37144         "propertychange": true
37145     });
37146     this.customEditors = this.customEditors || {};
37147 };
37148 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
37149     
37150      /**
37151      * @cfg {Object} customEditors map of colnames=> custom editors.
37152      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
37153      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
37154      * false disables editing of the field.
37155          */
37156     
37157       /**
37158      * @cfg {Object} propertyNames map of property Names to their displayed value
37159          */
37160     
37161     render : function(){
37162         Roo.grid.PropertyGrid.superclass.render.call(this);
37163         this.autoSize.defer(100, this);
37164     },
37165
37166     autoSize : function(){
37167         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
37168         if(this.view){
37169             this.view.fitColumns();
37170         }
37171     },
37172
37173     onColumnResize : function(){
37174         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
37175         this.autoSize();
37176     },
37177     /**
37178      * Sets the data for the Grid
37179      * accepts a Key => Value object of all the elements avaiable.
37180      * @param {Object} data  to appear in grid.
37181      */
37182     setSource : function(source){
37183         this.store.setSource(source);
37184         //this.autoSize();
37185     },
37186     /**
37187      * Gets all the data from the grid.
37188      * @return {Object} data  data stored in grid
37189      */
37190     getSource : function(){
37191         return this.store.getSource();
37192     }
37193 });/*
37194   
37195  * Licence LGPL
37196  
37197  */
37198  
37199 /**
37200  * @class Roo.grid.Calendar
37201  * @extends Roo.grid.Grid
37202  * This class extends the Grid to provide a calendar widget
37203  * <br><br>Usage:<pre><code>
37204  var grid = new Roo.grid.Calendar("my-container-id", {
37205      ds: myDataStore,
37206      cm: myColModel,
37207      selModel: mySelectionModel,
37208      autoSizeColumns: true,
37209      monitorWindowResize: false,
37210      trackMouseOver: true
37211      eventstore : real data store..
37212  });
37213  // set any options
37214  grid.render();
37215   
37216   * @constructor
37217  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37218  * The container MUST have some type of size defined for the grid to fill. The container will be
37219  * automatically set to position relative if it isn't already.
37220  * @param {Object} config A config object that sets properties on this grid.
37221  */
37222 Roo.grid.Calendar = function(container, config){
37223         // initialize the container
37224         this.container = Roo.get(container);
37225         this.container.update("");
37226         this.container.setStyle("overflow", "hidden");
37227     this.container.addClass('x-grid-container');
37228
37229     this.id = this.container.id;
37230
37231     Roo.apply(this, config);
37232     // check and correct shorthanded configs
37233     
37234     var rows = [];
37235     var d =1;
37236     for (var r = 0;r < 6;r++) {
37237         
37238         rows[r]=[];
37239         for (var c =0;c < 7;c++) {
37240             rows[r][c]= '';
37241         }
37242     }
37243     if (this.eventStore) {
37244         this.eventStore= Roo.factory(this.eventStore, Roo.data);
37245         this.eventStore.on('load',this.onLoad, this);
37246         this.eventStore.on('beforeload',this.clearEvents, this);
37247          
37248     }
37249     
37250     this.dataSource = new Roo.data.Store({
37251             proxy: new Roo.data.MemoryProxy(rows),
37252             reader: new Roo.data.ArrayReader({}, [
37253                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
37254     });
37255
37256     this.dataSource.load();
37257     this.ds = this.dataSource;
37258     this.ds.xmodule = this.xmodule || false;
37259     
37260     
37261     var cellRender = function(v,x,r)
37262     {
37263         return String.format(
37264             '<div class="fc-day  fc-widget-content"><div>' +
37265                 '<div class="fc-event-container"></div>' +
37266                 '<div class="fc-day-number">{0}</div>'+
37267                 
37268                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
37269             '</div></div>', v);
37270     
37271     }
37272     
37273     
37274     this.colModel = new Roo.grid.ColumnModel( [
37275         {
37276             xtype: 'ColumnModel',
37277             xns: Roo.grid,
37278             dataIndex : 'weekday0',
37279             header : 'Sunday',
37280             renderer : cellRender
37281         },
37282         {
37283             xtype: 'ColumnModel',
37284             xns: Roo.grid,
37285             dataIndex : 'weekday1',
37286             header : 'Monday',
37287             renderer : cellRender
37288         },
37289         {
37290             xtype: 'ColumnModel',
37291             xns: Roo.grid,
37292             dataIndex : 'weekday2',
37293             header : 'Tuesday',
37294             renderer : cellRender
37295         },
37296         {
37297             xtype: 'ColumnModel',
37298             xns: Roo.grid,
37299             dataIndex : 'weekday3',
37300             header : 'Wednesday',
37301             renderer : cellRender
37302         },
37303         {
37304             xtype: 'ColumnModel',
37305             xns: Roo.grid,
37306             dataIndex : 'weekday4',
37307             header : 'Thursday',
37308             renderer : cellRender
37309         },
37310         {
37311             xtype: 'ColumnModel',
37312             xns: Roo.grid,
37313             dataIndex : 'weekday5',
37314             header : 'Friday',
37315             renderer : cellRender
37316         },
37317         {
37318             xtype: 'ColumnModel',
37319             xns: Roo.grid,
37320             dataIndex : 'weekday6',
37321             header : 'Saturday',
37322             renderer : cellRender
37323         }
37324     ]);
37325     this.cm = this.colModel;
37326     this.cm.xmodule = this.xmodule || false;
37327  
37328         
37329           
37330     //this.selModel = new Roo.grid.CellSelectionModel();
37331     //this.sm = this.selModel;
37332     //this.selModel.init(this);
37333     
37334     
37335     if(this.width){
37336         this.container.setWidth(this.width);
37337     }
37338
37339     if(this.height){
37340         this.container.setHeight(this.height);
37341     }
37342     /** @private */
37343         this.addEvents({
37344         // raw events
37345         /**
37346          * @event click
37347          * The raw click event for the entire grid.
37348          * @param {Roo.EventObject} e
37349          */
37350         "click" : true,
37351         /**
37352          * @event dblclick
37353          * The raw dblclick event for the entire grid.
37354          * @param {Roo.EventObject} e
37355          */
37356         "dblclick" : true,
37357         /**
37358          * @event contextmenu
37359          * The raw contextmenu event for the entire grid.
37360          * @param {Roo.EventObject} e
37361          */
37362         "contextmenu" : true,
37363         /**
37364          * @event mousedown
37365          * The raw mousedown event for the entire grid.
37366          * @param {Roo.EventObject} e
37367          */
37368         "mousedown" : true,
37369         /**
37370          * @event mouseup
37371          * The raw mouseup event for the entire grid.
37372          * @param {Roo.EventObject} e
37373          */
37374         "mouseup" : true,
37375         /**
37376          * @event mouseover
37377          * The raw mouseover event for the entire grid.
37378          * @param {Roo.EventObject} e
37379          */
37380         "mouseover" : true,
37381         /**
37382          * @event mouseout
37383          * The raw mouseout event for the entire grid.
37384          * @param {Roo.EventObject} e
37385          */
37386         "mouseout" : true,
37387         /**
37388          * @event keypress
37389          * The raw keypress event for the entire grid.
37390          * @param {Roo.EventObject} e
37391          */
37392         "keypress" : true,
37393         /**
37394          * @event keydown
37395          * The raw keydown event for the entire grid.
37396          * @param {Roo.EventObject} e
37397          */
37398         "keydown" : true,
37399
37400         // custom events
37401
37402         /**
37403          * @event cellclick
37404          * Fires when a cell is clicked
37405          * @param {Grid} this
37406          * @param {Number} rowIndex
37407          * @param {Number} columnIndex
37408          * @param {Roo.EventObject} e
37409          */
37410         "cellclick" : true,
37411         /**
37412          * @event celldblclick
37413          * Fires when a cell is double clicked
37414          * @param {Grid} this
37415          * @param {Number} rowIndex
37416          * @param {Number} columnIndex
37417          * @param {Roo.EventObject} e
37418          */
37419         "celldblclick" : true,
37420         /**
37421          * @event rowclick
37422          * Fires when a row is clicked
37423          * @param {Grid} this
37424          * @param {Number} rowIndex
37425          * @param {Roo.EventObject} e
37426          */
37427         "rowclick" : true,
37428         /**
37429          * @event rowdblclick
37430          * Fires when a row is double clicked
37431          * @param {Grid} this
37432          * @param {Number} rowIndex
37433          * @param {Roo.EventObject} e
37434          */
37435         "rowdblclick" : true,
37436         /**
37437          * @event headerclick
37438          * Fires when a header is clicked
37439          * @param {Grid} this
37440          * @param {Number} columnIndex
37441          * @param {Roo.EventObject} e
37442          */
37443         "headerclick" : true,
37444         /**
37445          * @event headerdblclick
37446          * Fires when a header cell is double clicked
37447          * @param {Grid} this
37448          * @param {Number} columnIndex
37449          * @param {Roo.EventObject} e
37450          */
37451         "headerdblclick" : true,
37452         /**
37453          * @event rowcontextmenu
37454          * Fires when a row is right clicked
37455          * @param {Grid} this
37456          * @param {Number} rowIndex
37457          * @param {Roo.EventObject} e
37458          */
37459         "rowcontextmenu" : true,
37460         /**
37461          * @event cellcontextmenu
37462          * Fires when a cell is right clicked
37463          * @param {Grid} this
37464          * @param {Number} rowIndex
37465          * @param {Number} cellIndex
37466          * @param {Roo.EventObject} e
37467          */
37468          "cellcontextmenu" : true,
37469         /**
37470          * @event headercontextmenu
37471          * Fires when a header is right clicked
37472          * @param {Grid} this
37473          * @param {Number} columnIndex
37474          * @param {Roo.EventObject} e
37475          */
37476         "headercontextmenu" : true,
37477         /**
37478          * @event bodyscroll
37479          * Fires when the body element is scrolled
37480          * @param {Number} scrollLeft
37481          * @param {Number} scrollTop
37482          */
37483         "bodyscroll" : true,
37484         /**
37485          * @event columnresize
37486          * Fires when the user resizes a column
37487          * @param {Number} columnIndex
37488          * @param {Number} newSize
37489          */
37490         "columnresize" : true,
37491         /**
37492          * @event columnmove
37493          * Fires when the user moves a column
37494          * @param {Number} oldIndex
37495          * @param {Number} newIndex
37496          */
37497         "columnmove" : true,
37498         /**
37499          * @event startdrag
37500          * Fires when row(s) start being dragged
37501          * @param {Grid} this
37502          * @param {Roo.GridDD} dd The drag drop object
37503          * @param {event} e The raw browser event
37504          */
37505         "startdrag" : true,
37506         /**
37507          * @event enddrag
37508          * Fires when a drag operation is complete
37509          * @param {Grid} this
37510          * @param {Roo.GridDD} dd The drag drop object
37511          * @param {event} e The raw browser event
37512          */
37513         "enddrag" : true,
37514         /**
37515          * @event dragdrop
37516          * Fires when dragged row(s) are dropped on a valid DD target
37517          * @param {Grid} this
37518          * @param {Roo.GridDD} dd The drag drop object
37519          * @param {String} targetId The target drag drop object
37520          * @param {event} e The raw browser event
37521          */
37522         "dragdrop" : true,
37523         /**
37524          * @event dragover
37525          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
37526          * @param {Grid} this
37527          * @param {Roo.GridDD} dd The drag drop object
37528          * @param {String} targetId The target drag drop object
37529          * @param {event} e The raw browser event
37530          */
37531         "dragover" : true,
37532         /**
37533          * @event dragenter
37534          *  Fires when the dragged row(s) first cross another DD target while being dragged
37535          * @param {Grid} this
37536          * @param {Roo.GridDD} dd The drag drop object
37537          * @param {String} targetId The target drag drop object
37538          * @param {event} e The raw browser event
37539          */
37540         "dragenter" : true,
37541         /**
37542          * @event dragout
37543          * Fires when the dragged row(s) leave another DD target while being dragged
37544          * @param {Grid} this
37545          * @param {Roo.GridDD} dd The drag drop object
37546          * @param {String} targetId The target drag drop object
37547          * @param {event} e The raw browser event
37548          */
37549         "dragout" : true,
37550         /**
37551          * @event rowclass
37552          * Fires when a row is rendered, so you can change add a style to it.
37553          * @param {GridView} gridview   The grid view
37554          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
37555          */
37556         'rowclass' : true,
37557
37558         /**
37559          * @event render
37560          * Fires when the grid is rendered
37561          * @param {Grid} grid
37562          */
37563         'render' : true,
37564             /**
37565              * @event select
37566              * Fires when a date is selected
37567              * @param {DatePicker} this
37568              * @param {Date} date The selected date
37569              */
37570         'select': true,
37571         /**
37572              * @event monthchange
37573              * Fires when the displayed month changes 
37574              * @param {DatePicker} this
37575              * @param {Date} date The selected month
37576              */
37577         'monthchange': true,
37578         /**
37579              * @event evententer
37580              * Fires when mouse over an event
37581              * @param {Calendar} this
37582              * @param {event} Event
37583              */
37584         'evententer': true,
37585         /**
37586              * @event eventleave
37587              * Fires when the mouse leaves an
37588              * @param {Calendar} this
37589              * @param {event}
37590              */
37591         'eventleave': true,
37592         /**
37593              * @event eventclick
37594              * Fires when the mouse click an
37595              * @param {Calendar} this
37596              * @param {event}
37597              */
37598         'eventclick': true,
37599         /**
37600              * @event eventrender
37601              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
37602              * @param {Calendar} this
37603              * @param {data} data to be modified
37604              */
37605         'eventrender': true
37606         
37607     });
37608
37609     Roo.grid.Grid.superclass.constructor.call(this);
37610     this.on('render', function() {
37611         this.view.el.addClass('x-grid-cal'); 
37612         
37613         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
37614
37615     },this);
37616     
37617     if (!Roo.grid.Calendar.style) {
37618         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
37619             
37620             
37621             '.x-grid-cal .x-grid-col' :  {
37622                 height: 'auto !important',
37623                 'vertical-align': 'top'
37624             },
37625             '.x-grid-cal  .fc-event-hori' : {
37626                 height: '14px'
37627             }
37628              
37629             
37630         }, Roo.id());
37631     }
37632
37633     
37634     
37635 };
37636 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
37637     /**
37638      * @cfg {Store} eventStore The store that loads events.
37639      */
37640     eventStore : 25,
37641
37642      
37643     activeDate : false,
37644     startDay : 0,
37645     autoWidth : true,
37646     monitorWindowResize : false,
37647
37648     
37649     resizeColumns : function() {
37650         var col = (this.view.el.getWidth() / 7) - 3;
37651         // loop through cols, and setWidth
37652         for(var i =0 ; i < 7 ; i++){
37653             this.cm.setColumnWidth(i, col);
37654         }
37655     },
37656      setDate :function(date) {
37657         
37658         Roo.log('setDate?');
37659         
37660         this.resizeColumns();
37661         var vd = this.activeDate;
37662         this.activeDate = date;
37663 //        if(vd && this.el){
37664 //            var t = date.getTime();
37665 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37666 //                Roo.log('using add remove');
37667 //                
37668 //                this.fireEvent('monthchange', this, date);
37669 //                
37670 //                this.cells.removeClass("fc-state-highlight");
37671 //                this.cells.each(function(c){
37672 //                   if(c.dateValue == t){
37673 //                       c.addClass("fc-state-highlight");
37674 //                       setTimeout(function(){
37675 //                            try{c.dom.firstChild.focus();}catch(e){}
37676 //                       }, 50);
37677 //                       return false;
37678 //                   }
37679 //                   return true;
37680 //                });
37681 //                return;
37682 //            }
37683 //        }
37684         
37685         var days = date.getDaysInMonth();
37686         
37687         var firstOfMonth = date.getFirstDateOfMonth();
37688         var startingPos = firstOfMonth.getDay()-this.startDay;
37689         
37690         if(startingPos < this.startDay){
37691             startingPos += 7;
37692         }
37693         
37694         var pm = date.add(Date.MONTH, -1);
37695         var prevStart = pm.getDaysInMonth()-startingPos;
37696 //        
37697         
37698         
37699         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37700         
37701         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37702         //this.cells.addClassOnOver('fc-state-hover');
37703         
37704         var cells = this.cells.elements;
37705         var textEls = this.textNodes;
37706         
37707         //Roo.each(cells, function(cell){
37708         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37709         //});
37710         
37711         days += startingPos;
37712
37713         // convert everything to numbers so it's fast
37714         var day = 86400000;
37715         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37716         //Roo.log(d);
37717         //Roo.log(pm);
37718         //Roo.log(prevStart);
37719         
37720         var today = new Date().clearTime().getTime();
37721         var sel = date.clearTime().getTime();
37722         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37723         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37724         var ddMatch = this.disabledDatesRE;
37725         var ddText = this.disabledDatesText;
37726         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37727         var ddaysText = this.disabledDaysText;
37728         var format = this.format;
37729         
37730         var setCellClass = function(cal, cell){
37731             
37732             //Roo.log('set Cell Class');
37733             cell.title = "";
37734             var t = d.getTime();
37735             
37736             //Roo.log(d);
37737             
37738             
37739             cell.dateValue = t;
37740             if(t == today){
37741                 cell.className += " fc-today";
37742                 cell.className += " fc-state-highlight";
37743                 cell.title = cal.todayText;
37744             }
37745             if(t == sel){
37746                 // disable highlight in other month..
37747                 cell.className += " fc-state-highlight";
37748                 
37749             }
37750             // disabling
37751             if(t < min) {
37752                 //cell.className = " fc-state-disabled";
37753                 cell.title = cal.minText;
37754                 return;
37755             }
37756             if(t > max) {
37757                 //cell.className = " fc-state-disabled";
37758                 cell.title = cal.maxText;
37759                 return;
37760             }
37761             if(ddays){
37762                 if(ddays.indexOf(d.getDay()) != -1){
37763                     // cell.title = ddaysText;
37764                    // cell.className = " fc-state-disabled";
37765                 }
37766             }
37767             if(ddMatch && format){
37768                 var fvalue = d.dateFormat(format);
37769                 if(ddMatch.test(fvalue)){
37770                     cell.title = ddText.replace("%0", fvalue);
37771                    cell.className = " fc-state-disabled";
37772                 }
37773             }
37774             
37775             if (!cell.initialClassName) {
37776                 cell.initialClassName = cell.dom.className;
37777             }
37778             
37779             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
37780         };
37781
37782         var i = 0;
37783         
37784         for(; i < startingPos; i++) {
37785             cells[i].dayName =  (++prevStart);
37786             Roo.log(textEls[i]);
37787             d.setDate(d.getDate()+1);
37788             
37789             //cells[i].className = "fc-past fc-other-month";
37790             setCellClass(this, cells[i]);
37791         }
37792         
37793         var intDay = 0;
37794         
37795         for(; i < days; i++){
37796             intDay = i - startingPos + 1;
37797             cells[i].dayName =  (intDay);
37798             d.setDate(d.getDate()+1);
37799             
37800             cells[i].className = ''; // "x-date-active";
37801             setCellClass(this, cells[i]);
37802         }
37803         var extraDays = 0;
37804         
37805         for(; i < 42; i++) {
37806             //textEls[i].innerHTML = (++extraDays);
37807             
37808             d.setDate(d.getDate()+1);
37809             cells[i].dayName = (++extraDays);
37810             cells[i].className = "fc-future fc-other-month";
37811             setCellClass(this, cells[i]);
37812         }
37813         
37814         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37815         
37816         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37817         
37818         // this will cause all the cells to mis
37819         var rows= [];
37820         var i =0;
37821         for (var r = 0;r < 6;r++) {
37822             for (var c =0;c < 7;c++) {
37823                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37824             }    
37825         }
37826         
37827         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37828         for(i=0;i<cells.length;i++) {
37829             
37830             this.cells.elements[i].dayName = cells[i].dayName ;
37831             this.cells.elements[i].className = cells[i].className;
37832             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37833             this.cells.elements[i].title = cells[i].title ;
37834             this.cells.elements[i].dateValue = cells[i].dateValue ;
37835         }
37836         
37837         
37838         
37839         
37840         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37841         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37842         
37843         ////if(totalRows != 6){
37844             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37845            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37846        // }
37847         
37848         this.fireEvent('monthchange', this, date);
37849         
37850         
37851     },
37852  /**
37853      * Returns the grid's SelectionModel.
37854      * @return {SelectionModel}
37855      */
37856     getSelectionModel : function(){
37857         if(!this.selModel){
37858             this.selModel = new Roo.grid.CellSelectionModel();
37859         }
37860         return this.selModel;
37861     },
37862
37863     load: function() {
37864         this.eventStore.load()
37865         
37866         
37867         
37868     },
37869     
37870     findCell : function(dt) {
37871         dt = dt.clearTime().getTime();
37872         var ret = false;
37873         this.cells.each(function(c){
37874             //Roo.log("check " +c.dateValue + '?=' + dt);
37875             if(c.dateValue == dt){
37876                 ret = c;
37877                 return false;
37878             }
37879             return true;
37880         });
37881         
37882         return ret;
37883     },
37884     
37885     findCells : function(rec) {
37886         var s = rec.data.start_dt.clone().clearTime().getTime();
37887        // Roo.log(s);
37888         var e= rec.data.end_dt.clone().clearTime().getTime();
37889        // Roo.log(e);
37890         var ret = [];
37891         this.cells.each(function(c){
37892              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37893             
37894             if(c.dateValue > e){
37895                 return ;
37896             }
37897             if(c.dateValue < s){
37898                 return ;
37899             }
37900             ret.push(c);
37901         });
37902         
37903         return ret;    
37904     },
37905     
37906     findBestRow: function(cells)
37907     {
37908         var ret = 0;
37909         
37910         for (var i =0 ; i < cells.length;i++) {
37911             ret  = Math.max(cells[i].rows || 0,ret);
37912         }
37913         return ret;
37914         
37915     },
37916     
37917     
37918     addItem : function(rec)
37919     {
37920         // look for vertical location slot in
37921         var cells = this.findCells(rec);
37922         
37923         rec.row = this.findBestRow(cells);
37924         
37925         // work out the location.
37926         
37927         var crow = false;
37928         var rows = [];
37929         for(var i =0; i < cells.length; i++) {
37930             if (!crow) {
37931                 crow = {
37932                     start : cells[i],
37933                     end :  cells[i]
37934                 };
37935                 continue;
37936             }
37937             if (crow.start.getY() == cells[i].getY()) {
37938                 // on same row.
37939                 crow.end = cells[i];
37940                 continue;
37941             }
37942             // different row.
37943             rows.push(crow);
37944             crow = {
37945                 start: cells[i],
37946                 end : cells[i]
37947             };
37948             
37949         }
37950         
37951         rows.push(crow);
37952         rec.els = [];
37953         rec.rows = rows;
37954         rec.cells = cells;
37955         for (var i = 0; i < cells.length;i++) {
37956             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
37957             
37958         }
37959         
37960         
37961     },
37962     
37963     clearEvents: function() {
37964         
37965         if (!this.eventStore.getCount()) {
37966             return;
37967         }
37968         // reset number of rows in cells.
37969         Roo.each(this.cells.elements, function(c){
37970             c.rows = 0;
37971         });
37972         
37973         this.eventStore.each(function(e) {
37974             this.clearEvent(e);
37975         },this);
37976         
37977     },
37978     
37979     clearEvent : function(ev)
37980     {
37981         if (ev.els) {
37982             Roo.each(ev.els, function(el) {
37983                 el.un('mouseenter' ,this.onEventEnter, this);
37984                 el.un('mouseleave' ,this.onEventLeave, this);
37985                 el.remove();
37986             },this);
37987             ev.els = [];
37988         }
37989     },
37990     
37991     
37992     renderEvent : function(ev,ctr) {
37993         if (!ctr) {
37994              ctr = this.view.el.select('.fc-event-container',true).first();
37995         }
37996         
37997          
37998         this.clearEvent(ev);
37999             //code
38000        
38001         
38002         
38003         ev.els = [];
38004         var cells = ev.cells;
38005         var rows = ev.rows;
38006         this.fireEvent('eventrender', this, ev);
38007         
38008         for(var i =0; i < rows.length; i++) {
38009             
38010             cls = '';
38011             if (i == 0) {
38012                 cls += ' fc-event-start';
38013             }
38014             if ((i+1) == rows.length) {
38015                 cls += ' fc-event-end';
38016             }
38017             
38018             //Roo.log(ev.data);
38019             // how many rows should it span..
38020             var cg = this.eventTmpl.append(ctr,Roo.apply({
38021                 fccls : cls
38022                 
38023             }, ev.data) , true);
38024             
38025             
38026             cg.on('mouseenter' ,this.onEventEnter, this, ev);
38027             cg.on('mouseleave' ,this.onEventLeave, this, ev);
38028             cg.on('click', this.onEventClick, this, ev);
38029             
38030             ev.els.push(cg);
38031             
38032             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
38033             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
38034             //Roo.log(cg);
38035              
38036             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
38037             cg.setWidth(ebox.right - sbox.x -2);
38038         }
38039     },
38040     
38041     renderEvents: function()
38042     {   
38043         // first make sure there is enough space..
38044         
38045         if (!this.eventTmpl) {
38046             this.eventTmpl = new Roo.Template(
38047                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
38048                     '<div class="fc-event-inner">' +
38049                         '<span class="fc-event-time">{time}</span>' +
38050                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
38051                     '</div>' +
38052                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
38053                 '</div>'
38054             );
38055                 
38056         }
38057                
38058         
38059         
38060         this.cells.each(function(c) {
38061             //Roo.log(c.select('.fc-day-content div',true).first());
38062             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
38063         });
38064         
38065         var ctr = this.view.el.select('.fc-event-container',true).first();
38066         
38067         var cls;
38068         this.eventStore.each(function(ev){
38069             
38070             this.renderEvent(ev);
38071              
38072              
38073         }, this);
38074         this.view.layout();
38075         
38076     },
38077     
38078     onEventEnter: function (e, el,event,d) {
38079         this.fireEvent('evententer', this, el, event);
38080     },
38081     
38082     onEventLeave: function (e, el,event,d) {
38083         this.fireEvent('eventleave', this, el, event);
38084     },
38085     
38086     onEventClick: function (e, el,event,d) {
38087         this.fireEvent('eventclick', this, el, event);
38088     },
38089     
38090     onMonthChange: function () {
38091         this.store.load();
38092     },
38093     
38094     onLoad: function () {
38095         
38096         //Roo.log('calendar onload');
38097 //         
38098         if(this.eventStore.getCount() > 0){
38099             
38100            
38101             
38102             this.eventStore.each(function(d){
38103                 
38104                 
38105                 // FIXME..
38106                 var add =   d.data;
38107                 if (typeof(add.end_dt) == 'undefined')  {
38108                     Roo.log("Missing End time in calendar data: ");
38109                     Roo.log(d);
38110                     return;
38111                 }
38112                 if (typeof(add.start_dt) == 'undefined')  {
38113                     Roo.log("Missing Start time in calendar data: ");
38114                     Roo.log(d);
38115                     return;
38116                 }
38117                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
38118                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
38119                 add.id = add.id || d.id;
38120                 add.title = add.title || '??';
38121                 
38122                 this.addItem(d);
38123                 
38124              
38125             },this);
38126         }
38127         
38128         this.renderEvents();
38129     }
38130     
38131
38132 });
38133 /*
38134  grid : {
38135                 xtype: 'Grid',
38136                 xns: Roo.grid,
38137                 listeners : {
38138                     render : function ()
38139                     {
38140                         _this.grid = this;
38141                         
38142                         if (!this.view.el.hasClass('course-timesheet')) {
38143                             this.view.el.addClass('course-timesheet');
38144                         }
38145                         if (this.tsStyle) {
38146                             this.ds.load({});
38147                             return; 
38148                         }
38149                         Roo.log('width');
38150                         Roo.log(_this.grid.view.el.getWidth());
38151                         
38152                         
38153                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
38154                             '.course-timesheet .x-grid-row' : {
38155                                 height: '80px'
38156                             },
38157                             '.x-grid-row td' : {
38158                                 'vertical-align' : 0
38159                             },
38160                             '.course-edit-link' : {
38161                                 'color' : 'blue',
38162                                 'text-overflow' : 'ellipsis',
38163                                 'overflow' : 'hidden',
38164                                 'white-space' : 'nowrap',
38165                                 'cursor' : 'pointer'
38166                             },
38167                             '.sub-link' : {
38168                                 'color' : 'green'
38169                             },
38170                             '.de-act-sup-link' : {
38171                                 'color' : 'purple',
38172                                 'text-decoration' : 'line-through'
38173                             },
38174                             '.de-act-link' : {
38175                                 'color' : 'red',
38176                                 'text-decoration' : 'line-through'
38177                             },
38178                             '.course-timesheet .course-highlight' : {
38179                                 'border-top-style': 'dashed !important',
38180                                 'border-bottom-bottom': 'dashed !important'
38181                             },
38182                             '.course-timesheet .course-item' : {
38183                                 'font-family'   : 'tahoma, arial, helvetica',
38184                                 'font-size'     : '11px',
38185                                 'overflow'      : 'hidden',
38186                                 'padding-left'  : '10px',
38187                                 'padding-right' : '10px',
38188                                 'padding-top' : '10px' 
38189                             }
38190                             
38191                         }, Roo.id());
38192                                 this.ds.load({});
38193                     }
38194                 },
38195                 autoWidth : true,
38196                 monitorWindowResize : false,
38197                 cellrenderer : function(v,x,r)
38198                 {
38199                     return v;
38200                 },
38201                 sm : {
38202                     xtype: 'CellSelectionModel',
38203                     xns: Roo.grid
38204                 },
38205                 dataSource : {
38206                     xtype: 'Store',
38207                     xns: Roo.data,
38208                     listeners : {
38209                         beforeload : function (_self, options)
38210                         {
38211                             options.params = options.params || {};
38212                             options.params._month = _this.monthField.getValue();
38213                             options.params.limit = 9999;
38214                             options.params['sort'] = 'when_dt';    
38215                             options.params['dir'] = 'ASC';    
38216                             this.proxy.loadResponse = this.loadResponse;
38217                             Roo.log("load?");
38218                             //this.addColumns();
38219                         },
38220                         load : function (_self, records, options)
38221                         {
38222                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
38223                                 // if you click on the translation.. you can edit it...
38224                                 var el = Roo.get(this);
38225                                 var id = el.dom.getAttribute('data-id');
38226                                 var d = el.dom.getAttribute('data-date');
38227                                 var t = el.dom.getAttribute('data-time');
38228                                 //var id = this.child('span').dom.textContent;
38229                                 
38230                                 //Roo.log(this);
38231                                 Pman.Dialog.CourseCalendar.show({
38232                                     id : id,
38233                                     when_d : d,
38234                                     when_t : t,
38235                                     productitem_active : id ? 1 : 0
38236                                 }, function() {
38237                                     _this.grid.ds.load({});
38238                                 });
38239                            
38240                            });
38241                            
38242                            _this.panel.fireEvent('resize', [ '', '' ]);
38243                         }
38244                     },
38245                     loadResponse : function(o, success, response){
38246                             // this is overridden on before load..
38247                             
38248                             Roo.log("our code?");       
38249                             //Roo.log(success);
38250                             //Roo.log(response)
38251                             delete this.activeRequest;
38252                             if(!success){
38253                                 this.fireEvent("loadexception", this, o, response);
38254                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38255                                 return;
38256                             }
38257                             var result;
38258                             try {
38259                                 result = o.reader.read(response);
38260                             }catch(e){
38261                                 Roo.log("load exception?");
38262                                 this.fireEvent("loadexception", this, o, response, e);
38263                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38264                                 return;
38265                             }
38266                             Roo.log("ready...");        
38267                             // loop through result.records;
38268                             // and set this.tdate[date] = [] << array of records..
38269                             _this.tdata  = {};
38270                             Roo.each(result.records, function(r){
38271                                 //Roo.log(r.data);
38272                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
38273                                     _this.tdata[r.data.when_dt.format('j')] = [];
38274                                 }
38275                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
38276                             });
38277                             
38278                             //Roo.log(_this.tdata);
38279                             
38280                             result.records = [];
38281                             result.totalRecords = 6;
38282                     
38283                             // let's generate some duumy records for the rows.
38284                             //var st = _this.dateField.getValue();
38285                             
38286                             // work out monday..
38287                             //st = st.add(Date.DAY, -1 * st.format('w'));
38288                             
38289                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38290                             
38291                             var firstOfMonth = date.getFirstDayOfMonth();
38292                             var days = date.getDaysInMonth();
38293                             var d = 1;
38294                             var firstAdded = false;
38295                             for (var i = 0; i < result.totalRecords ; i++) {
38296                                 //var d= st.add(Date.DAY, i);
38297                                 var row = {};
38298                                 var added = 0;
38299                                 for(var w = 0 ; w < 7 ; w++){
38300                                     if(!firstAdded && firstOfMonth != w){
38301                                         continue;
38302                                     }
38303                                     if(d > days){
38304                                         continue;
38305                                     }
38306                                     firstAdded = true;
38307                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
38308                                     row['weekday'+w] = String.format(
38309                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
38310                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
38311                                                     d,
38312                                                     date.format('Y-m-')+dd
38313                                                 );
38314                                     added++;
38315                                     if(typeof(_this.tdata[d]) != 'undefined'){
38316                                         Roo.each(_this.tdata[d], function(r){
38317                                             var is_sub = '';
38318                                             var deactive = '';
38319                                             var id = r.id;
38320                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
38321                                             if(r.parent_id*1>0){
38322                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
38323                                                 id = r.parent_id;
38324                                             }
38325                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
38326                                                 deactive = 'de-act-link';
38327                                             }
38328                                             
38329                                             row['weekday'+w] += String.format(
38330                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
38331                                                     id, //0
38332                                                     r.product_id_name, //1
38333                                                     r.when_dt.format('h:ia'), //2
38334                                                     is_sub, //3
38335                                                     deactive, //4
38336                                                     desc // 5
38337                                             );
38338                                         });
38339                                     }
38340                                     d++;
38341                                 }
38342                                 
38343                                 // only do this if something added..
38344                                 if(added > 0){ 
38345                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
38346                                 }
38347                                 
38348                                 
38349                                 // push it twice. (second one with an hour..
38350                                 
38351                             }
38352                             //Roo.log(result);
38353                             this.fireEvent("load", this, o, o.request.arg);
38354                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
38355                         },
38356                     sortInfo : {field: 'when_dt', direction : 'ASC' },
38357                     proxy : {
38358                         xtype: 'HttpProxy',
38359                         xns: Roo.data,
38360                         method : 'GET',
38361                         url : baseURL + '/Roo/Shop_course.php'
38362                     },
38363                     reader : {
38364                         xtype: 'JsonReader',
38365                         xns: Roo.data,
38366                         id : 'id',
38367                         fields : [
38368                             {
38369                                 'name': 'id',
38370                                 'type': 'int'
38371                             },
38372                             {
38373                                 'name': 'when_dt',
38374                                 'type': 'string'
38375                             },
38376                             {
38377                                 'name': 'end_dt',
38378                                 'type': 'string'
38379                             },
38380                             {
38381                                 'name': 'parent_id',
38382                                 'type': 'int'
38383                             },
38384                             {
38385                                 'name': 'product_id',
38386                                 'type': 'int'
38387                             },
38388                             {
38389                                 'name': 'productitem_id',
38390                                 'type': 'int'
38391                             },
38392                             {
38393                                 'name': 'guid',
38394                                 'type': 'int'
38395                             }
38396                         ]
38397                     }
38398                 },
38399                 toolbar : {
38400                     xtype: 'Toolbar',
38401                     xns: Roo,
38402                     items : [
38403                         {
38404                             xtype: 'Button',
38405                             xns: Roo.Toolbar,
38406                             listeners : {
38407                                 click : function (_self, e)
38408                                 {
38409                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38410                                     sd.setMonth(sd.getMonth()-1);
38411                                     _this.monthField.setValue(sd.format('Y-m-d'));
38412                                     _this.grid.ds.load({});
38413                                 }
38414                             },
38415                             text : "Back"
38416                         },
38417                         {
38418                             xtype: 'Separator',
38419                             xns: Roo.Toolbar
38420                         },
38421                         {
38422                             xtype: 'MonthField',
38423                             xns: Roo.form,
38424                             listeners : {
38425                                 render : function (_self)
38426                                 {
38427                                     _this.monthField = _self;
38428                                    // _this.monthField.set  today
38429                                 },
38430                                 select : function (combo, date)
38431                                 {
38432                                     _this.grid.ds.load({});
38433                                 }
38434                             },
38435                             value : (function() { return new Date(); })()
38436                         },
38437                         {
38438                             xtype: 'Separator',
38439                             xns: Roo.Toolbar
38440                         },
38441                         {
38442                             xtype: 'TextItem',
38443                             xns: Roo.Toolbar,
38444                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
38445                         },
38446                         {
38447                             xtype: 'Fill',
38448                             xns: Roo.Toolbar
38449                         },
38450                         {
38451                             xtype: 'Button',
38452                             xns: Roo.Toolbar,
38453                             listeners : {
38454                                 click : function (_self, e)
38455                                 {
38456                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38457                                     sd.setMonth(sd.getMonth()+1);
38458                                     _this.monthField.setValue(sd.format('Y-m-d'));
38459                                     _this.grid.ds.load({});
38460                                 }
38461                             },
38462                             text : "Next"
38463                         }
38464                     ]
38465                 },
38466                  
38467             }
38468         };
38469         
38470         *//*
38471  * Based on:
38472  * Ext JS Library 1.1.1
38473  * Copyright(c) 2006-2007, Ext JS, LLC.
38474  *
38475  * Originally Released Under LGPL - original licence link has changed is not relivant.
38476  *
38477  * Fork - LGPL
38478  * <script type="text/javascript">
38479  */
38480  
38481 /**
38482  * @class Roo.LoadMask
38483  * A simple utility class for generically masking elements while loading data.  If the element being masked has
38484  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38485  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
38486  * element's UpdateManager load indicator and will be destroyed after the initial load.
38487  * @constructor
38488  * Create a new LoadMask
38489  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38490  * @param {Object} config The config object
38491  */
38492 Roo.LoadMask = function(el, config){
38493     this.el = Roo.get(el);
38494     Roo.apply(this, config);
38495     if(this.store){
38496         this.store.on('beforeload', this.onBeforeLoad, this);
38497         this.store.on('load', this.onLoad, this);
38498         this.store.on('loadexception', this.onLoadException, this);
38499         this.removeMask = false;
38500     }else{
38501         var um = this.el.getUpdateManager();
38502         um.showLoadIndicator = false; // disable the default indicator
38503         um.on('beforeupdate', this.onBeforeLoad, this);
38504         um.on('update', this.onLoad, this);
38505         um.on('failure', this.onLoad, this);
38506         this.removeMask = true;
38507     }
38508 };
38509
38510 Roo.LoadMask.prototype = {
38511     /**
38512      * @cfg {Boolean} removeMask
38513      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38514      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
38515      */
38516     removeMask : false,
38517     /**
38518      * @cfg {String} msg
38519      * The text to display in a centered loading message box (defaults to 'Loading...')
38520      */
38521     msg : 'Loading...',
38522     /**
38523      * @cfg {String} msgCls
38524      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38525      */
38526     msgCls : 'x-mask-loading',
38527
38528     /**
38529      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38530      * @type Boolean
38531      */
38532     disabled: false,
38533
38534     /**
38535      * Disables the mask to prevent it from being displayed
38536      */
38537     disable : function(){
38538        this.disabled = true;
38539     },
38540
38541     /**
38542      * Enables the mask so that it can be displayed
38543      */
38544     enable : function(){
38545         this.disabled = false;
38546     },
38547     
38548     onLoadException : function()
38549     {
38550         Roo.log(arguments);
38551         
38552         if (typeof(arguments[3]) != 'undefined') {
38553             Roo.MessageBox.alert("Error loading",arguments[3]);
38554         } 
38555         /*
38556         try {
38557             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38558                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38559             }   
38560         } catch(e) {
38561             
38562         }
38563         */
38564     
38565         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38566     },
38567     // private
38568     onLoad : function()
38569     {
38570         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38571     },
38572
38573     // private
38574     onBeforeLoad : function(){
38575         if(!this.disabled){
38576             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
38577         }
38578     },
38579
38580     // private
38581     destroy : function(){
38582         if(this.store){
38583             this.store.un('beforeload', this.onBeforeLoad, this);
38584             this.store.un('load', this.onLoad, this);
38585             this.store.un('loadexception', this.onLoadException, this);
38586         }else{
38587             var um = this.el.getUpdateManager();
38588             um.un('beforeupdate', this.onBeforeLoad, this);
38589             um.un('update', this.onLoad, this);
38590             um.un('failure', this.onLoad, this);
38591         }
38592     }
38593 };/*
38594  * Based on:
38595  * Ext JS Library 1.1.1
38596  * Copyright(c) 2006-2007, Ext JS, LLC.
38597  *
38598  * Originally Released Under LGPL - original licence link has changed is not relivant.
38599  *
38600  * Fork - LGPL
38601  * <script type="text/javascript">
38602  */
38603
38604
38605 /**
38606  * @class Roo.XTemplate
38607  * @extends Roo.Template
38608  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38609 <pre><code>
38610 var t = new Roo.XTemplate(
38611         '&lt;select name="{name}"&gt;',
38612                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
38613         '&lt;/select&gt;'
38614 );
38615  
38616 // then append, applying the master template values
38617  </code></pre>
38618  *
38619  * Supported features:
38620  *
38621  *  Tags:
38622
38623 <pre><code>
38624       {a_variable} - output encoded.
38625       {a_variable.format:("Y-m-d")} - call a method on the variable
38626       {a_variable:raw} - unencoded output
38627       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38628       {a_variable:this.method_on_template(...)} - call a method on the template object.
38629  
38630 </code></pre>
38631  *  The tpl tag:
38632 <pre><code>
38633         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
38634         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
38635         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
38636         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
38637   
38638         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
38639         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
38640 </code></pre>
38641  *      
38642  */
38643 Roo.XTemplate = function()
38644 {
38645     Roo.XTemplate.superclass.constructor.apply(this, arguments);
38646     if (this.html) {
38647         this.compile();
38648     }
38649 };
38650
38651
38652 Roo.extend(Roo.XTemplate, Roo.Template, {
38653
38654     /**
38655      * The various sub templates
38656      */
38657     tpls : false,
38658     /**
38659      *
38660      * basic tag replacing syntax
38661      * WORD:WORD()
38662      *
38663      * // you can fake an object call by doing this
38664      *  x.t:(test,tesT) 
38665      * 
38666      */
38667     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38668
38669     /**
38670      * compile the template
38671      *
38672      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38673      *
38674      */
38675     compile: function()
38676     {
38677         var s = this.html;
38678      
38679         s = ['<tpl>', s, '</tpl>'].join('');
38680     
38681         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38682             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38683             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38684             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38685             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38686             m,
38687             id     = 0,
38688             tpls   = [];
38689     
38690         while(true == !!(m = s.match(re))){
38691             var forMatch   = m[0].match(nameRe),
38692                 ifMatch   = m[0].match(ifRe),
38693                 execMatch   = m[0].match(execRe),
38694                 namedMatch   = m[0].match(namedRe),
38695                 
38696                 exp  = null, 
38697                 fn   = null,
38698                 exec = null,
38699                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38700                 
38701             if (ifMatch) {
38702                 // if - puts fn into test..
38703                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38704                 if(exp){
38705                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38706                 }
38707             }
38708             
38709             if (execMatch) {
38710                 // exec - calls a function... returns empty if true is  returned.
38711                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38712                 if(exp){
38713                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38714                 }
38715             }
38716             
38717             
38718             if (name) {
38719                 // for = 
38720                 switch(name){
38721                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38722                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38723                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38724                 }
38725             }
38726             var uid = namedMatch ? namedMatch[1] : id;
38727             
38728             
38729             tpls.push({
38730                 id:     namedMatch ? namedMatch[1] : id,
38731                 target: name,
38732                 exec:   exec,
38733                 test:   fn,
38734                 body:   m[1] || ''
38735             });
38736             if (namedMatch) {
38737                 s = s.replace(m[0], '');
38738             } else { 
38739                 s = s.replace(m[0], '{xtpl'+ id + '}');
38740             }
38741             ++id;
38742         }
38743         this.tpls = [];
38744         for(var i = tpls.length-1; i >= 0; --i){
38745             this.compileTpl(tpls[i]);
38746             this.tpls[tpls[i].id] = tpls[i];
38747         }
38748         this.master = tpls[tpls.length-1];
38749         return this;
38750     },
38751     /**
38752      * same as applyTemplate, except it's done to one of the subTemplates
38753      * when using named templates, you can do:
38754      *
38755      * var str = pl.applySubTemplate('your-name', values);
38756      *
38757      * 
38758      * @param {Number} id of the template
38759      * @param {Object} values to apply to template
38760      * @param {Object} parent (normaly the instance of this object)
38761      */
38762     applySubTemplate : function(id, values, parent)
38763     {
38764         
38765         
38766         var t = this.tpls[id];
38767         
38768         
38769         try { 
38770             if(t.test && !t.test.call(this, values, parent)){
38771                 return '';
38772             }
38773         } catch(e) {
38774             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38775             Roo.log(e.toString());
38776             Roo.log(t.test);
38777             return ''
38778         }
38779         try { 
38780             
38781             if(t.exec && t.exec.call(this, values, parent)){
38782                 return '';
38783             }
38784         } catch(e) {
38785             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38786             Roo.log(e.toString());
38787             Roo.log(t.exec);
38788             return ''
38789         }
38790         try {
38791             var vs = t.target ? t.target.call(this, values, parent) : values;
38792             parent = t.target ? values : parent;
38793             if(t.target && vs instanceof Array){
38794                 var buf = [];
38795                 for(var i = 0, len = vs.length; i < len; i++){
38796                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38797                 }
38798                 return buf.join('');
38799             }
38800             return t.compiled.call(this, vs, parent);
38801         } catch (e) {
38802             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38803             Roo.log(e.toString());
38804             Roo.log(t.compiled);
38805             return '';
38806         }
38807     },
38808
38809     compileTpl : function(tpl)
38810     {
38811         var fm = Roo.util.Format;
38812         var useF = this.disableFormats !== true;
38813         var sep = Roo.isGecko ? "+" : ",";
38814         var undef = function(str) {
38815             Roo.log("Property not found :"  + str);
38816             return '';
38817         };
38818         
38819         var fn = function(m, name, format, args)
38820         {
38821             //Roo.log(arguments);
38822             args = args ? args.replace(/\\'/g,"'") : args;
38823             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38824             if (typeof(format) == 'undefined') {
38825                 format= 'htmlEncode';
38826             }
38827             if (format == 'raw' ) {
38828                 format = false;
38829             }
38830             
38831             if(name.substr(0, 4) == 'xtpl'){
38832                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38833             }
38834             
38835             // build an array of options to determine if value is undefined..
38836             
38837             // basically get 'xxxx.yyyy' then do
38838             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38839             //    (function () { Roo.log("Property not found"); return ''; })() :
38840             //    ......
38841             
38842             var udef_ar = [];
38843             var lookfor = '';
38844             Roo.each(name.split('.'), function(st) {
38845                 lookfor += (lookfor.length ? '.': '') + st;
38846                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38847             });
38848             
38849             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38850             
38851             
38852             if(format && useF){
38853                 
38854                 args = args ? ',' + args : "";
38855                  
38856                 if(format.substr(0, 5) != "this."){
38857                     format = "fm." + format + '(';
38858                 }else{
38859                     format = 'this.call("'+ format.substr(5) + '", ';
38860                     args = ", values";
38861                 }
38862                 
38863                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38864             }
38865              
38866             if (args.length) {
38867                 // called with xxyx.yuu:(test,test)
38868                 // change to ()
38869                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38870             }
38871             // raw.. - :raw modifier..
38872             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38873             
38874         };
38875         var body;
38876         // branched to use + in gecko and [].join() in others
38877         if(Roo.isGecko){
38878             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38879                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38880                     "';};};";
38881         }else{
38882             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38883             body.push(tpl.body.replace(/(\r\n|\n)/g,
38884                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38885             body.push("'].join('');};};");
38886             body = body.join('');
38887         }
38888         
38889         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38890        
38891         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38892         eval(body);
38893         
38894         return this;
38895     },
38896
38897     applyTemplate : function(values){
38898         return this.master.compiled.call(this, values, {});
38899         //var s = this.subs;
38900     },
38901
38902     apply : function(){
38903         return this.applyTemplate.apply(this, arguments);
38904     }
38905
38906  });
38907
38908 Roo.XTemplate.from = function(el){
38909     el = Roo.getDom(el);
38910     return new Roo.XTemplate(el.value || el.innerHTML);
38911 };