c9116f332cabaa764b2694b9cbf942f92f7b16e1
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13 /**
14  * @class Roo.data.SortTypes
15  * @singleton
16  * Defines the default sorting (casting?) comparison functions used when sorting data.
17  */
18 Roo.data.SortTypes = {
19     /**
20      * Default sort that does nothing
21      * @param {Mixed} s The value being converted
22      * @return {Mixed} The comparison value
23      */
24     none : function(s){
25         return s;
26     },
27     
28     /**
29      * The regular expression used to strip tags
30      * @type {RegExp}
31      * @property
32      */
33     stripTagsRE : /<\/?[^>]+>/gi,
34     
35     /**
36      * Strips all HTML tags to sort on text only
37      * @param {Mixed} s The value being converted
38      * @return {String} The comparison value
39      */
40     asText : function(s){
41         return String(s).replace(this.stripTagsRE, "");
42     },
43     
44     /**
45      * Strips all HTML tags to sort on text only - Case insensitive
46      * @param {Mixed} s The value being converted
47      * @return {String} The comparison value
48      */
49     asUCText : function(s){
50         return String(s).toUpperCase().replace(this.stripTagsRE, "");
51     },
52     
53     /**
54      * Case insensitive string
55      * @param {Mixed} s The value being converted
56      * @return {String} The comparison value
57      */
58     asUCString : function(s) {
59         return String(s).toUpperCase();
60     },
61     
62     /**
63      * Date sorting
64      * @param {Mixed} s The value being converted
65      * @return {Number} The comparison value
66      */
67     asDate : function(s) {
68         if(!s){
69             return 0;
70         }
71         if(s instanceof Date){
72             return s.getTime();
73         }
74         return Date.parse(String(s));
75     },
76     
77     /**
78      * Float sorting
79      * @param {Mixed} s The value being converted
80      * @return {Float} The comparison value
81      */
82     asFloat : function(s) {
83         var val = parseFloat(String(s).replace(/,/g, ""));
84         if(isNaN(val)) {
85             val = 0;
86         }
87         return val;
88     },
89     
90     /**
91      * Integer sorting
92      * @param {Mixed} s The value being converted
93      * @return {Number} The comparison value
94      */
95     asInt : function(s) {
96         var val = parseInt(String(s).replace(/,/g, ""));
97         if(isNaN(val)) {
98             val = 0;
99         }
100         return val;
101     }
102 };/*
103  * Based on:
104  * Ext JS Library 1.1.1
105  * Copyright(c) 2006-2007, Ext JS, LLC.
106  *
107  * Originally Released Under LGPL - original licence link has changed is not relivant.
108  *
109  * Fork - LGPL
110  * <script type="text/javascript">
111  */
112
113 /**
114 * @class Roo.data.Record
115  * Instances of this class encapsulate both record <em>definition</em> information, and record
116  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
117  * to access Records cached in an {@link Roo.data.Store} object.<br>
118  * <p>
119  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
120  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
121  * objects.<br>
122  * <p>
123  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
124  * @constructor
125  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
126  * {@link #create}. The parameters are the same.
127  * @param {Array} data An associative Array of data values keyed by the field name.
128  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
129  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
130  * not specified an integer id is generated.
131  */
132 Roo.data.Record = function(data, id){
133     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
134     this.data = data;
135 };
136
137 /**
138  * Generate a constructor for a specific record layout.
139  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
140  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
141  * Each field definition object may contain the following properties: <ul>
142  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
143  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
144  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
145  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
146  * is being used, then this is a string containing the javascript expression to reference the data relative to 
147  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
148  * to the data item relative to the record element. If the mapping expression is the same as the field name,
149  * this may be omitted.</p></li>
150  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
151  * <ul><li>auto (Default, implies no conversion)</li>
152  * <li>string</li>
153  * <li>int</li>
154  * <li>float</li>
155  * <li>boolean</li>
156  * <li>date</li></ul></p></li>
157  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
158  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
159  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
160  * by the Reader into an object that will be stored in the Record. It is passed the
161  * following parameters:<ul>
162  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
163  * </ul></p></li>
164  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
165  * </ul>
166  * <br>usage:<br><pre><code>
167 var TopicRecord = Roo.data.Record.create(
168     {name: 'title', mapping: 'topic_title'},
169     {name: 'author', mapping: 'username'},
170     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
171     {name: 'lastPost', mapping: 'post_time', type: 'date'},
172     {name: 'lastPoster', mapping: 'user2'},
173     {name: 'excerpt', mapping: 'post_text'}
174 );
175
176 var myNewRecord = new TopicRecord({
177     title: 'Do my job please',
178     author: 'noobie',
179     totalPosts: 1,
180     lastPost: new Date(),
181     lastPoster: 'Animal',
182     excerpt: 'No way dude!'
183 });
184 myStore.add(myNewRecord);
185 </code></pre>
186  * @method create
187  * @static
188  */
189 Roo.data.Record.create = function(o){
190     var f = function(){
191         f.superclass.constructor.apply(this, arguments);
192     };
193     Roo.extend(f, Roo.data.Record);
194     var p = f.prototype;
195     p.fields = new Roo.util.MixedCollection(false, function(field){
196         return field.name;
197     });
198     for(var i = 0, len = o.length; i < len; i++){
199         p.fields.add(new Roo.data.Field(o[i]));
200     }
201     f.getField = function(name){
202         return p.fields.get(name);  
203     };
204     return f;
205 };
206
207 Roo.data.Record.AUTO_ID = 1000;
208 Roo.data.Record.EDIT = 'edit';
209 Roo.data.Record.REJECT = 'reject';
210 Roo.data.Record.COMMIT = 'commit';
211
212 Roo.data.Record.prototype = {
213     /**
214      * Readonly flag - true if this record has been modified.
215      * @type Boolean
216      */
217     dirty : false,
218     editing : false,
219     error: null,
220     modified: null,
221
222     // private
223     join : function(store){
224         this.store = store;
225     },
226
227     /**
228      * Set the named field to the specified value.
229      * @param {String} name The name of the field to set.
230      * @param {Object} value The value to set the field to.
231      */
232     set : function(name, value){
233         if(this.data[name] == value){
234             return;
235         }
236         this.dirty = true;
237         if(!this.modified){
238             this.modified = {};
239         }
240         if(typeof this.modified[name] == 'undefined'){
241             this.modified[name] = this.data[name];
242         }
243         this.data[name] = value;
244         if(!this.editing && this.store){
245             this.store.afterEdit(this);
246         }       
247     },
248
249     /**
250      * Get the value of the named field.
251      * @param {String} name The name of the field to get the value of.
252      * @return {Object} The value of the field.
253      */
254     get : function(name){
255         return this.data[name]; 
256     },
257
258     // private
259     beginEdit : function(){
260         this.editing = true;
261         this.modified = {}; 
262     },
263
264     // private
265     cancelEdit : function(){
266         this.editing = false;
267         delete this.modified;
268     },
269
270     // private
271     endEdit : function(){
272         this.editing = false;
273         if(this.dirty && this.store){
274             this.store.afterEdit(this);
275         }
276     },
277
278     /**
279      * Usually called by the {@link Roo.data.Store} which owns the Record.
280      * Rejects all changes made to the Record since either creation, or the last commit operation.
281      * Modified fields are reverted to their original values.
282      * <p>
283      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
284      * of reject operations.
285      */
286     reject : function(){
287         var m = this.modified;
288         for(var n in m){
289             if(typeof m[n] != "function"){
290                 this.data[n] = m[n];
291             }
292         }
293         this.dirty = false;
294         delete this.modified;
295         this.editing = false;
296         if(this.store){
297             this.store.afterReject(this);
298         }
299     },
300
301     /**
302      * Usually called by the {@link Roo.data.Store} which owns the Record.
303      * Commits all changes made to the Record since either creation, or the last commit operation.
304      * <p>
305      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
306      * of commit operations.
307      */
308     commit : function(){
309         this.dirty = false;
310         delete this.modified;
311         this.editing = false;
312         if(this.store){
313             this.store.afterCommit(this);
314         }
315     },
316
317     // private
318     hasError : function(){
319         return this.error != null;
320     },
321
322     // private
323     clearError : function(){
324         this.error = null;
325     },
326
327     /**
328      * Creates a copy of this record.
329      * @param {String} id (optional) A new record id if you don't want to use this record's id
330      * @return {Record}
331      */
332     copy : function(newId) {
333         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
334     }
335 };/*
336  * Based on:
337  * Ext JS Library 1.1.1
338  * Copyright(c) 2006-2007, Ext JS, LLC.
339  *
340  * Originally Released Under LGPL - original licence link has changed is not relivant.
341  *
342  * Fork - LGPL
343  * <script type="text/javascript">
344  */
345
346
347
348 /**
349  * @class Roo.data.Store
350  * @extends Roo.util.Observable
351  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
352  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
353  * <p>
354  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
355  * has no knowledge of the format of the data returned by the Proxy.<br>
356  * <p>
357  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
358  * instances from the data object. These records are cached and made available through accessor functions.
359  * @constructor
360  * Creates a new Store.
361  * @param {Object} config A config object containing the objects needed for the Store to access data,
362  * and read the data into Records.
363  */
364 Roo.data.Store = function(config){
365     this.data = new Roo.util.MixedCollection(false);
366     this.data.getKey = function(o){
367         return o.id;
368     };
369     this.baseParams = {};
370     // private
371     this.paramNames = {
372         "start" : "start",
373         "limit" : "limit",
374         "sort" : "sort",
375         "dir" : "dir",
376         "multisort" : "_multisort"
377     };
378
379     if(config && config.data){
380         this.inlineData = config.data;
381         delete config.data;
382     }
383
384     Roo.apply(this, config);
385     
386     if(this.reader){ // reader passed
387         this.reader = Roo.factory(this.reader, Roo.data);
388         this.reader.xmodule = this.xmodule || false;
389         if(!this.recordType){
390             this.recordType = this.reader.recordType;
391         }
392         if(this.reader.onMetaChange){
393             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
394         }
395     }
396
397     if(this.recordType){
398         this.fields = this.recordType.prototype.fields;
399     }
400     this.modified = [];
401
402     this.addEvents({
403         /**
404          * @event datachanged
405          * Fires when the data cache has changed, and a widget which is using this Store
406          * as a Record cache should refresh its view.
407          * @param {Store} this
408          */
409         datachanged : true,
410         /**
411          * @event metachange
412          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
413          * @param {Store} this
414          * @param {Object} meta The JSON metadata
415          */
416         metachange : true,
417         /**
418          * @event add
419          * Fires when Records have been added to the Store
420          * @param {Store} this
421          * @param {Roo.data.Record[]} records The array of Records added
422          * @param {Number} index The index at which the record(s) were added
423          */
424         add : true,
425         /**
426          * @event remove
427          * Fires when a Record has been removed from the Store
428          * @param {Store} this
429          * @param {Roo.data.Record} record The Record that was removed
430          * @param {Number} index The index at which the record was removed
431          */
432         remove : true,
433         /**
434          * @event update
435          * Fires when a Record has been updated
436          * @param {Store} this
437          * @param {Roo.data.Record} record The Record that was updated
438          * @param {String} operation The update operation being performed.  Value may be one of:
439          * <pre><code>
440  Roo.data.Record.EDIT
441  Roo.data.Record.REJECT
442  Roo.data.Record.COMMIT
443          * </code></pre>
444          */
445         update : true,
446         /**
447          * @event clear
448          * Fires when the data cache has been cleared.
449          * @param {Store} this
450          */
451         clear : true,
452         /**
453          * @event beforeload
454          * Fires before a request is made for a new data object.  If the beforeload handler returns false
455          * the load action will be canceled.
456          * @param {Store} this
457          * @param {Object} options The loading options that were specified (see {@link #load} for details)
458          */
459         beforeload : true,
460         /**
461          * @event beforeloadadd
462          * Fires after a new set of Records has been loaded.
463          * @param {Store} this
464          * @param {Roo.data.Record[]} records The Records that were loaded
465          * @param {Object} options The loading options that were specified (see {@link #load} for details)
466          */
467         beforeloadadd : true,
468         /**
469          * @event load
470          * Fires after a new set of Records has been loaded, before they are added to the store.
471          * @param {Store} this
472          * @param {Roo.data.Record[]} records The Records that were loaded
473          * @param {Object} options The loading options that were specified (see {@link #load} for details)
474          * @params {Object} return from reader
475          */
476         load : true,
477         /**
478          * @event loadexception
479          * Fires if an exception occurs in the Proxy during loading.
480          * Called with the signature of the Proxy's "loadexception" event.
481          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
482          * 
483          * @param {Proxy} 
484          * @param {Object} return from JsonData.reader() - success, totalRecords, records
485          * @param {Object} load options 
486          * @param {Object} jsonData from your request (normally this contains the Exception)
487          */
488         loadexception : true
489     });
490     
491     if(this.proxy){
492         this.proxy = Roo.factory(this.proxy, Roo.data);
493         this.proxy.xmodule = this.xmodule || false;
494         this.relayEvents(this.proxy,  ["loadexception"]);
495     }
496     this.sortToggle = {};
497     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
498
499     Roo.data.Store.superclass.constructor.call(this);
500
501     if(this.inlineData){
502         this.loadData(this.inlineData);
503         delete this.inlineData;
504     }
505 };
506
507 Roo.extend(Roo.data.Store, Roo.util.Observable, {
508      /**
509     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
510     * without a remote query - used by combo/forms at present.
511     */
512     
513     /**
514     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
515     */
516     /**
517     * @cfg {Array} data Inline data to be loaded when the store is initialized.
518     */
519     /**
520     * @cfg {Roo.data.Reader} reader [required]  The Reader object which processes the data object and returns
521     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
522     */
523     /**
524     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
525     * on any HTTP request
526     */
527     /**
528     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
529     */
530     /**
531     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
532     */
533     multiSort: false,
534     /**
535     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
536     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
537     */
538     remoteSort : false,
539
540     /**
541     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
542      * loaded or when a record is removed. (defaults to false).
543     */
544     pruneModifiedRecords : false,
545
546     // private
547     lastOptions : null,
548
549     /**
550      * Add Records to the Store and fires the add event.
551      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
552      */
553     add : function(records){
554         records = [].concat(records);
555         for(var i = 0, len = records.length; i < len; i++){
556             records[i].join(this);
557         }
558         var index = this.data.length;
559         this.data.addAll(records);
560         this.fireEvent("add", this, records, index);
561     },
562
563     /**
564      * Remove a Record from the Store and fires the remove event.
565      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
566      */
567     remove : function(record){
568         var index = this.data.indexOf(record);
569         this.data.removeAt(index);
570  
571         if(this.pruneModifiedRecords){
572             this.modified.remove(record);
573         }
574         this.fireEvent("remove", this, record, index);
575     },
576
577     /**
578      * Remove all Records from the Store and fires the clear event.
579      */
580     removeAll : function(){
581         this.data.clear();
582         if(this.pruneModifiedRecords){
583             this.modified = [];
584         }
585         this.fireEvent("clear", this);
586     },
587
588     /**
589      * Inserts Records to the Store at the given index and fires the add event.
590      * @param {Number} index The start index at which to insert the passed Records.
591      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
592      */
593     insert : function(index, records){
594         records = [].concat(records);
595         for(var i = 0, len = records.length; i < len; i++){
596             this.data.insert(index, records[i]);
597             records[i].join(this);
598         }
599         this.fireEvent("add", this, records, index);
600     },
601
602     /**
603      * Get the index within the cache of the passed Record.
604      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
605      * @return {Number} The index of the passed Record. Returns -1 if not found.
606      */
607     indexOf : function(record){
608         return this.data.indexOf(record);
609     },
610
611     /**
612      * Get the index within the cache of the Record with the passed id.
613      * @param {String} id The id of the Record to find.
614      * @return {Number} The index of the Record. Returns -1 if not found.
615      */
616     indexOfId : function(id){
617         return this.data.indexOfKey(id);
618     },
619
620     /**
621      * Get the Record with the specified id.
622      * @param {String} id The id of the Record to find.
623      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
624      */
625     getById : function(id){
626         return this.data.key(id);
627     },
628
629     /**
630      * Get the Record at the specified index.
631      * @param {Number} index The index of the Record to find.
632      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
633      */
634     getAt : function(index){
635         return this.data.itemAt(index);
636     },
637
638     /**
639      * Returns a range of Records between specified indices.
640      * @param {Number} startIndex (optional) The starting index (defaults to 0)
641      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
642      * @return {Roo.data.Record[]} An array of Records
643      */
644     getRange : function(start, end){
645         return this.data.getRange(start, end);
646     },
647
648     // private
649     storeOptions : function(o){
650         o = Roo.apply({}, o);
651         delete o.callback;
652         delete o.scope;
653         this.lastOptions = o;
654     },
655
656     /**
657      * Loads the Record cache from the configured Proxy using the configured Reader.
658      * <p>
659      * If using remote paging, then the first load call must specify the <em>start</em>
660      * and <em>limit</em> properties in the options.params property to establish the initial
661      * position within the dataset, and the number of Records to cache on each read from the Proxy.
662      * <p>
663      * <strong>It is important to note that for remote data sources, loading is asynchronous,
664      * and this call will return before the new data has been loaded. Perform any post-processing
665      * in a callback function, or in a "load" event handler.</strong>
666      * <p>
667      * @param {Object} options An object containing properties which control loading options:<ul>
668      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
669      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
670      * passed the following arguments:<ul>
671      * <li>r : Roo.data.Record[]</li>
672      * <li>options: Options object from the load call</li>
673      * <li>success: Boolean success indicator</li></ul></li>
674      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
675      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
676      * </ul>
677      */
678     load : function(options){
679         options = options || {};
680         if(this.fireEvent("beforeload", this, options) !== false){
681             this.storeOptions(options);
682             var p = Roo.apply(options.params || {}, this.baseParams);
683             // if meta was not loaded from remote source.. try requesting it.
684             if (!this.reader.metaFromRemote) {
685                 p._requestMeta = 1;
686             }
687             if(this.sortInfo && this.remoteSort){
688                 var pn = this.paramNames;
689                 p[pn["sort"]] = this.sortInfo.field;
690                 p[pn["dir"]] = this.sortInfo.direction;
691             }
692             if (this.multiSort) {
693                 var pn = this.paramNames;
694                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
695             }
696             
697             this.proxy.load(p, this.reader, this.loadRecords, this, options);
698         }
699     },
700
701     /**
702      * Reloads the Record cache from the configured Proxy using the configured Reader and
703      * the options from the last load operation performed.
704      * @param {Object} options (optional) An object containing properties which may override the options
705      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
706      * the most recently used options are reused).
707      */
708     reload : function(options){
709         this.load(Roo.applyIf(options||{}, this.lastOptions));
710     },
711
712     // private
713     // Called as a callback by the Reader during a load operation.
714     loadRecords : function(o, options, success){
715         if(!o || success === false){
716             if(success !== false){
717                 this.fireEvent("load", this, [], options, o);
718             }
719             if(options.callback){
720                 options.callback.call(options.scope || this, [], options, false);
721             }
722             return;
723         }
724         // if data returned failure - throw an exception.
725         if (o.success === false) {
726             // show a message if no listener is registered.
727             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
728                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
729             }
730             // loadmask wil be hooked into this..
731             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
732             return;
733         }
734         var r = o.records, t = o.totalRecords || r.length;
735         
736         this.fireEvent("beforeloadadd", this, r, options, o);
737         
738         if(!options || options.add !== true){
739             if(this.pruneModifiedRecords){
740                 this.modified = [];
741             }
742             for(var i = 0, len = r.length; i < len; i++){
743                 r[i].join(this);
744             }
745             if(this.snapshot){
746                 this.data = this.snapshot;
747                 delete this.snapshot;
748             }
749             this.data.clear();
750             this.data.addAll(r);
751             this.totalLength = t;
752             this.applySort();
753             this.fireEvent("datachanged", this);
754         }else{
755             this.totalLength = Math.max(t, this.data.length+r.length);
756             this.add(r);
757         }
758         
759         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
760                 
761             var e = new Roo.data.Record({});
762
763             e.set(this.parent.displayField, this.parent.emptyTitle);
764             e.set(this.parent.valueField, '');
765
766             this.insert(0, e);
767         }
768             
769         this.fireEvent("load", this, r, options, o);
770         if(options.callback){
771             options.callback.call(options.scope || this, r, options, true);
772         }
773     },
774
775
776     /**
777      * Loads data from a passed data block. A Reader which understands the format of the data
778      * must have been configured in the constructor.
779      * @param {Object} data The data block from which to read the Records.  The format of the data expected
780      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
781      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
782      */
783     loadData : function(o, append){
784         var r = this.reader.readRecords(o);
785         this.loadRecords(r, {add: append}, true);
786     },
787     
788      /**
789      * using 'cn' the nested child reader read the child array into it's child stores.
790      * @param {Object} rec The record with a 'children array
791      */
792     loadDataFromChildren : function(rec)
793     {
794         this.loadData(this.reader.toLoadData(rec));
795     },
796     
797
798     /**
799      * Gets the number of cached records.
800      * <p>
801      * <em>If using paging, this may not be the total size of the dataset. If the data object
802      * used by the Reader contains the dataset size, then the getTotalCount() function returns
803      * the data set size</em>
804      */
805     getCount : function(){
806         return this.data.length || 0;
807     },
808
809     /**
810      * Gets the total number of records in the dataset as returned by the server.
811      * <p>
812      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
813      * the dataset size</em>
814      */
815     getTotalCount : function(){
816         return this.totalLength || 0;
817     },
818
819     /**
820      * Returns the sort state of the Store as an object with two properties:
821      * <pre><code>
822  field {String} The name of the field by which the Records are sorted
823  direction {String} The sort order, "ASC" or "DESC"
824      * </code></pre>
825      */
826     getSortState : function(){
827         return this.sortInfo;
828     },
829
830     // private
831     applySort : function(){
832         if(this.sortInfo && !this.remoteSort){
833             var s = this.sortInfo, f = s.field;
834             var st = this.fields.get(f).sortType;
835             var fn = function(r1, r2){
836                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
837                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
838             };
839             this.data.sort(s.direction, fn);
840             if(this.snapshot && this.snapshot != this.data){
841                 this.snapshot.sort(s.direction, fn);
842             }
843         }
844     },
845
846     /**
847      * Sets the default sort column and order to be used by the next load operation.
848      * @param {String} fieldName The name of the field to sort by.
849      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
850      */
851     setDefaultSort : function(field, dir){
852         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
853     },
854
855     /**
856      * Sort the Records.
857      * If remote sorting is used, the sort is performed on the server, and the cache is
858      * reloaded. If local sorting is used, the cache is sorted internally.
859      * @param {String} fieldName The name of the field to sort by.
860      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
861      */
862     sort : function(fieldName, dir){
863         var f = this.fields.get(fieldName);
864         if(!dir){
865             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
866             
867             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
868                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
869             }else{
870                 dir = f.sortDir;
871             }
872         }
873         this.sortToggle[f.name] = dir;
874         this.sortInfo = {field: f.name, direction: dir};
875         if(!this.remoteSort){
876             this.applySort();
877             this.fireEvent("datachanged", this);
878         }else{
879             this.load(this.lastOptions);
880         }
881     },
882
883     /**
884      * Calls the specified function for each of the Records in the cache.
885      * @param {Function} fn The function to call. The Record is passed as the first parameter.
886      * Returning <em>false</em> aborts and exits the iteration.
887      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
888      */
889     each : function(fn, scope){
890         this.data.each(fn, scope);
891     },
892
893     /**
894      * Gets all records modified since the last commit.  Modified records are persisted across load operations
895      * (e.g., during paging).
896      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
897      */
898     getModifiedRecords : function(){
899         return this.modified;
900     },
901
902     // private
903     createFilterFn : function(property, value, anyMatch){
904         if(!value.exec){ // not a regex
905             value = String(value);
906             if(value.length == 0){
907                 return false;
908             }
909             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
910         }
911         return function(r){
912             return value.test(r.data[property]);
913         };
914     },
915
916     /**
917      * Sums the value of <i>property</i> for each record between start and end and returns the result.
918      * @param {String} property A field on your records
919      * @param {Number} start The record index to start at (defaults to 0)
920      * @param {Number} end The last record index to include (defaults to length - 1)
921      * @return {Number} The sum
922      */
923     sum : function(property, start, end){
924         var rs = this.data.items, v = 0;
925         start = start || 0;
926         end = (end || end === 0) ? end : rs.length-1;
927
928         for(var i = start; i <= end; i++){
929             v += (rs[i].data[property] || 0);
930         }
931         return v;
932     },
933
934     /**
935      * Filter the records by a specified property.
936      * @param {String} field A field on your records
937      * @param {String/RegExp} value Either a string that the field
938      * should start with or a RegExp to test against the field
939      * @param {Boolean} anyMatch True to match any part not just the beginning
940      */
941     filter : function(property, value, anyMatch){
942         var fn = this.createFilterFn(property, value, anyMatch);
943         return fn ? this.filterBy(fn) : this.clearFilter();
944     },
945
946     /**
947      * Filter by a function. The specified function will be called with each
948      * record in this data source. If the function returns true the record is included,
949      * otherwise it is filtered.
950      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
951      * @param {Object} scope (optional) The scope of the function (defaults to this)
952      */
953     filterBy : function(fn, scope){
954         this.snapshot = this.snapshot || this.data;
955         this.data = this.queryBy(fn, scope||this);
956         this.fireEvent("datachanged", this);
957     },
958
959     /**
960      * Query the records by a specified property.
961      * @param {String} field A field on your records
962      * @param {String/RegExp} value Either a string that the field
963      * should start with or a RegExp to test against the field
964      * @param {Boolean} anyMatch True to match any part not just the beginning
965      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
966      */
967     query : function(property, value, anyMatch){
968         var fn = this.createFilterFn(property, value, anyMatch);
969         return fn ? this.queryBy(fn) : this.data.clone();
970     },
971
972     /**
973      * Query by a function. The specified function will be called with each
974      * record in this data source. If the function returns true the record is included
975      * in the results.
976      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
977      * @param {Object} scope (optional) The scope of the function (defaults to this)
978       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
979      **/
980     queryBy : function(fn, scope){
981         var data = this.snapshot || this.data;
982         return data.filterBy(fn, scope||this);
983     },
984
985     /**
986      * Collects unique values for a particular dataIndex from this store.
987      * @param {String} dataIndex The property to collect
988      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
989      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
990      * @return {Array} An array of the unique values
991      **/
992     collect : function(dataIndex, allowNull, bypassFilter){
993         var d = (bypassFilter === true && this.snapshot) ?
994                 this.snapshot.items : this.data.items;
995         var v, sv, r = [], l = {};
996         for(var i = 0, len = d.length; i < len; i++){
997             v = d[i].data[dataIndex];
998             sv = String(v);
999             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
1000                 l[sv] = true;
1001                 r[r.length] = v;
1002             }
1003         }
1004         return r;
1005     },
1006
1007     /**
1008      * Revert to a view of the Record cache with no filtering applied.
1009      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
1010      */
1011     clearFilter : function(suppressEvent){
1012         if(this.snapshot && this.snapshot != this.data){
1013             this.data = this.snapshot;
1014             delete this.snapshot;
1015             if(suppressEvent !== true){
1016                 this.fireEvent("datachanged", this);
1017             }
1018         }
1019     },
1020
1021     // private
1022     afterEdit : function(record){
1023         if(this.modified.indexOf(record) == -1){
1024             this.modified.push(record);
1025         }
1026         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1027     },
1028     
1029     // private
1030     afterReject : function(record){
1031         this.modified.remove(record);
1032         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1033     },
1034
1035     // private
1036     afterCommit : function(record){
1037         this.modified.remove(record);
1038         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1039     },
1040
1041     /**
1042      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1043      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1044      */
1045     commitChanges : function(){
1046         var m = this.modified.slice(0);
1047         this.modified = [];
1048         for(var i = 0, len = m.length; i < len; i++){
1049             m[i].commit();
1050         }
1051     },
1052
1053     /**
1054      * Cancel outstanding changes on all changed records.
1055      */
1056     rejectChanges : function(){
1057         var m = this.modified.slice(0);
1058         this.modified = [];
1059         for(var i = 0, len = m.length; i < len; i++){
1060             m[i].reject();
1061         }
1062     },
1063
1064     onMetaChange : function(meta, rtype, o){
1065         this.recordType = rtype;
1066         this.fields = rtype.prototype.fields;
1067         delete this.snapshot;
1068         this.sortInfo = meta.sortInfo || this.sortInfo;
1069         this.modified = [];
1070         this.fireEvent('metachange', this, this.reader.meta);
1071     },
1072     
1073     moveIndex : function(data, type)
1074     {
1075         var index = this.indexOf(data);
1076         
1077         var newIndex = index + type;
1078         
1079         this.remove(data);
1080         
1081         this.insert(newIndex, data);
1082         
1083     }
1084 });/*
1085  * Based on:
1086  * Ext JS Library 1.1.1
1087  * Copyright(c) 2006-2007, Ext JS, LLC.
1088  *
1089  * Originally Released Under LGPL - original licence link has changed is not relivant.
1090  *
1091  * Fork - LGPL
1092  * <script type="text/javascript">
1093  */
1094
1095 /**
1096  * @class Roo.data.SimpleStore
1097  * @extends Roo.data.Store
1098  * Small helper class to make creating Stores from Array data easier.
1099  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1100  * @cfg {Array} fields An array of field definition objects, or field name strings.
1101  * @cfg {Object} an existing reader (eg. copied from another store)
1102  * @cfg {Array} data The multi-dimensional array of data
1103  * @cfg {Roo.data.DataProxy} proxy [not-required]  
1104  * @cfg {Roo.data.Reader} reader  [not-required] 
1105  * @constructor
1106  * @param {Object} config
1107  */
1108 Roo.data.SimpleStore = function(config)
1109 {
1110     Roo.data.SimpleStore.superclass.constructor.call(this, {
1111         isLocal : true,
1112         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
1113                 id: config.id
1114             },
1115             Roo.data.Record.create(config.fields)
1116         ),
1117         proxy : new Roo.data.MemoryProxy(config.data)
1118     });
1119     this.load();
1120 };
1121 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1122  * Based on:
1123  * Ext JS Library 1.1.1
1124  * Copyright(c) 2006-2007, Ext JS, LLC.
1125  *
1126  * Originally Released Under LGPL - original licence link has changed is not relivant.
1127  *
1128  * Fork - LGPL
1129  * <script type="text/javascript">
1130  */
1131
1132 /**
1133 /**
1134  * @extends Roo.data.Store
1135  * @class Roo.data.JsonStore
1136  * Small helper class to make creating Stores for JSON data easier. <br/>
1137 <pre><code>
1138 var store = new Roo.data.JsonStore({
1139     url: 'get-images.php',
1140     root: 'images',
1141     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1142 });
1143 </code></pre>
1144  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1145  * JsonReader and HttpProxy (unless inline data is provided).</b>
1146  * @cfg {Array} fields An array of field definition objects, or field name strings.
1147  * @constructor
1148  * @param {Object} config
1149  */
1150 Roo.data.JsonStore = function(c){
1151     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1152         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1153         reader: new Roo.data.JsonReader(c, c.fields)
1154     }));
1155 };
1156 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1157  * Based on:
1158  * Ext JS Library 1.1.1
1159  * Copyright(c) 2006-2007, Ext JS, LLC.
1160  *
1161  * Originally Released Under LGPL - original licence link has changed is not relivant.
1162  *
1163  * Fork - LGPL
1164  * <script type="text/javascript">
1165  */
1166
1167  
1168 Roo.data.Field = function(config){
1169     if(typeof config == "string"){
1170         config = {name: config};
1171     }
1172     Roo.apply(this, config);
1173     
1174     if(!this.type){
1175         this.type = "auto";
1176     }
1177     
1178     var st = Roo.data.SortTypes;
1179     // named sortTypes are supported, here we look them up
1180     if(typeof this.sortType == "string"){
1181         this.sortType = st[this.sortType];
1182     }
1183     
1184     // set default sortType for strings and dates
1185     if(!this.sortType){
1186         switch(this.type){
1187             case "string":
1188                 this.sortType = st.asUCString;
1189                 break;
1190             case "date":
1191                 this.sortType = st.asDate;
1192                 break;
1193             default:
1194                 this.sortType = st.none;
1195         }
1196     }
1197
1198     // define once
1199     var stripRe = /[\$,%]/g;
1200
1201     // prebuilt conversion function for this field, instead of
1202     // switching every time we're reading a value
1203     if(!this.convert){
1204         var cv, dateFormat = this.dateFormat;
1205         switch(this.type){
1206             case "":
1207             case "auto":
1208             case undefined:
1209                 cv = function(v){ return v; };
1210                 break;
1211             case "string":
1212                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1213                 break;
1214             case "int":
1215                 cv = function(v){
1216                     return v !== undefined && v !== null && v !== '' ?
1217                            parseInt(String(v).replace(stripRe, ""), 10) : '';
1218                     };
1219                 break;
1220             case "float":
1221                 cv = function(v){
1222                     return v !== undefined && v !== null && v !== '' ?
1223                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
1224                     };
1225                 break;
1226             case "bool":
1227             case "boolean":
1228                 cv = function(v){ return v === true || v === "true" || v == 1; };
1229                 break;
1230             case "date":
1231                 cv = function(v){
1232                     if(!v){
1233                         return '';
1234                     }
1235                     if(v instanceof Date){
1236                         return v;
1237                     }
1238                     if(dateFormat){
1239                         if(dateFormat == "timestamp"){
1240                             return new Date(v*1000);
1241                         }
1242                         return Date.parseDate(v, dateFormat);
1243                     }
1244                     var parsed = Date.parse(v);
1245                     return parsed ? new Date(parsed) : null;
1246                 };
1247              break;
1248             
1249         }
1250         this.convert = cv;
1251     }
1252 };
1253
1254 Roo.data.Field.prototype = {
1255     dateFormat: null,
1256     defaultValue: "",
1257     mapping: null,
1258     sortType : null,
1259     sortDir : "ASC"
1260 };/*
1261  * Based on:
1262  * Ext JS Library 1.1.1
1263  * Copyright(c) 2006-2007, Ext JS, LLC.
1264  *
1265  * Originally Released Under LGPL - original licence link has changed is not relivant.
1266  *
1267  * Fork - LGPL
1268  * <script type="text/javascript">
1269  */
1270  
1271 // Base class for reading structured data from a data source.  This class is intended to be
1272 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1273
1274 /**
1275  * @class Roo.data.DataReader
1276  * Base class for reading structured data from a data source.  This class is intended to be
1277  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1278  */
1279
1280 Roo.data.DataReader = function(meta, recordType){
1281     
1282     this.meta = meta;
1283     
1284     this.recordType = recordType instanceof Array ? 
1285         Roo.data.Record.create(recordType) : recordType;
1286 };
1287
1288 Roo.data.DataReader.prototype = {
1289     
1290     
1291     readerType : 'Data',
1292      /**
1293      * Create an empty record
1294      * @param {Object} data (optional) - overlay some values
1295      * @return {Roo.data.Record} record created.
1296      */
1297     newRow :  function(d) {
1298         var da =  {};
1299         this.recordType.prototype.fields.each(function(c) {
1300             switch( c.type) {
1301                 case 'int' : da[c.name] = 0; break;
1302                 case 'date' : da[c.name] = new Date(); break;
1303                 case 'float' : da[c.name] = 0.0; break;
1304                 case 'boolean' : da[c.name] = false; break;
1305                 default : da[c.name] = ""; break;
1306             }
1307             
1308         });
1309         return new this.recordType(Roo.apply(da, d));
1310     }
1311     
1312     
1313 };/*
1314  * Based on:
1315  * Ext JS Library 1.1.1
1316  * Copyright(c) 2006-2007, Ext JS, LLC.
1317  *
1318  * Originally Released Under LGPL - original licence link has changed is not relivant.
1319  *
1320  * Fork - LGPL
1321  * <script type="text/javascript">
1322  */
1323
1324 /**
1325  * @class Roo.data.DataProxy
1326  * @extends Roo.data.Observable
1327  * @abstract
1328  * This class is an abstract base class for implementations which provide retrieval of
1329  * unformatted data objects.<br>
1330  * <p>
1331  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1332  * (of the appropriate type which knows how to parse the data object) to provide a block of
1333  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1334  * <p>
1335  * Custom implementations must implement the load method as described in
1336  * {@link Roo.data.HttpProxy#load}.
1337  */
1338 Roo.data.DataProxy = function(){
1339     this.addEvents({
1340         /**
1341          * @event beforeload
1342          * Fires before a network request is made to retrieve a data object.
1343          * @param {Object} This DataProxy object.
1344          * @param {Object} params The params parameter to the load function.
1345          */
1346         beforeload : true,
1347         /**
1348          * @event load
1349          * Fires before the load method's callback is called.
1350          * @param {Object} This DataProxy object.
1351          * @param {Object} o The data object.
1352          * @param {Object} arg The callback argument object passed to the load function.
1353          */
1354         load : true,
1355         /**
1356          * @event loadexception
1357          * Fires if an Exception occurs during data retrieval.
1358          * @param {Object} This DataProxy object.
1359          * @param {Object} o The data object.
1360          * @param {Object} arg The callback argument object passed to the load function.
1361          * @param {Object} e The Exception.
1362          */
1363         loadexception : true
1364     });
1365     Roo.data.DataProxy.superclass.constructor.call(this);
1366 };
1367
1368 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1369
1370     /**
1371      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1372      */
1373 /*
1374  * Based on:
1375  * Ext JS Library 1.1.1
1376  * Copyright(c) 2006-2007, Ext JS, LLC.
1377  *
1378  * Originally Released Under LGPL - original licence link has changed is not relivant.
1379  *
1380  * Fork - LGPL
1381  * <script type="text/javascript">
1382  */
1383 /**
1384  * @class Roo.data.MemoryProxy
1385  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1386  * to the Reader when its load method is called.
1387  * @constructor
1388  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1389  */
1390 Roo.data.MemoryProxy = function(data){
1391     if (data.data) {
1392         data = data.data;
1393     }
1394     Roo.data.MemoryProxy.superclass.constructor.call(this);
1395     this.data = data;
1396 };
1397
1398 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1399     
1400     /**
1401      * Load data from the requested source (in this case an in-memory
1402      * data object passed to the constructor), read the data object into
1403      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1404      * process that block using the passed callback.
1405      * @param {Object} params This parameter is not used by the MemoryProxy class.
1406      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1407      * object into a block of Roo.data.Records.
1408      * @param {Function} callback The function into which to pass the block of Roo.data.records.
1409      * The function must be passed <ul>
1410      * <li>The Record block object</li>
1411      * <li>The "arg" argument from the load function</li>
1412      * <li>A boolean success indicator</li>
1413      * </ul>
1414      * @param {Object} scope The scope in which to call the callback
1415      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1416      */
1417     load : function(params, reader, callback, scope, arg){
1418         params = params || {};
1419         var result;
1420         try {
1421             result = reader.readRecords(params.data ? params.data :this.data);
1422         }catch(e){
1423             this.fireEvent("loadexception", this, arg, null, e);
1424             callback.call(scope, null, arg, false);
1425             return;
1426         }
1427         callback.call(scope, result, arg, true);
1428     },
1429     
1430     // private
1431     update : function(params, records){
1432         
1433     }
1434 });/*
1435  * Based on:
1436  * Ext JS Library 1.1.1
1437  * Copyright(c) 2006-2007, Ext JS, LLC.
1438  *
1439  * Originally Released Under LGPL - original licence link has changed is not relivant.
1440  *
1441  * Fork - LGPL
1442  * <script type="text/javascript">
1443  */
1444 /**
1445  * @class Roo.data.HttpProxy
1446  * @extends Roo.data.DataProxy
1447  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1448  * configured to reference a certain URL.<br><br>
1449  * <p>
1450  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1451  * from which the running page was served.<br><br>
1452  * <p>
1453  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1454  * <p>
1455  * Be aware that to enable the browser to parse an XML document, the server must set
1456  * the Content-Type header in the HTTP response to "text/xml".
1457  * @constructor
1458  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1459  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
1460  * will be used to make the request.
1461  */
1462 Roo.data.HttpProxy = function(conn){
1463     Roo.data.HttpProxy.superclass.constructor.call(this);
1464     // is conn a conn config or a real conn?
1465     this.conn = conn;
1466     this.useAjax = !conn || !conn.events;
1467   
1468 };
1469
1470 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1471     // thse are take from connection...
1472     
1473     /**
1474      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1475      */
1476     /**
1477      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1478      * extra parameters to each request made by this object. (defaults to undefined)
1479      */
1480     /**
1481      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1482      *  to each request made by this object. (defaults to undefined)
1483      */
1484     /**
1485      * @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)
1486      */
1487     /**
1488      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1489      */
1490      /**
1491      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1492      * @type Boolean
1493      */
1494   
1495
1496     /**
1497      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1498      * @type Boolean
1499      */
1500     /**
1501      * Return the {@link Roo.data.Connection} object being used by this Proxy.
1502      * @return {Connection} The Connection object. This object may be used to subscribe to events on
1503      * a finer-grained basis than the DataProxy events.
1504      */
1505     getConnection : function(){
1506         return this.useAjax ? Roo.Ajax : this.conn;
1507     },
1508
1509     /**
1510      * Load data from the configured {@link Roo.data.Connection}, read the data object into
1511      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1512      * process that block using the passed callback.
1513      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1514      * for the request to the remote server.
1515      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1516      * object into a block of Roo.data.Records.
1517      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1518      * The function must be passed <ul>
1519      * <li>The Record block object</li>
1520      * <li>The "arg" argument from the load function</li>
1521      * <li>A boolean success indicator</li>
1522      * </ul>
1523      * @param {Object} scope The scope in which to call the callback
1524      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1525      */
1526     load : function(params, reader, callback, scope, arg){
1527         if(this.fireEvent("beforeload", this, params) !== false){
1528             var  o = {
1529                 params : params || {},
1530                 request: {
1531                     callback : callback,
1532                     scope : scope,
1533                     arg : arg
1534                 },
1535                 reader: reader,
1536                 callback : this.loadResponse,
1537                 scope: this
1538             };
1539             if(this.useAjax){
1540                 Roo.applyIf(o, this.conn);
1541                 if(this.activeRequest){
1542                     Roo.Ajax.abort(this.activeRequest);
1543                 }
1544                 this.activeRequest = Roo.Ajax.request(o);
1545             }else{
1546                 this.conn.request(o);
1547             }
1548         }else{
1549             callback.call(scope||this, null, arg, false);
1550         }
1551     },
1552
1553     // private
1554     loadResponse : function(o, success, response){
1555         delete this.activeRequest;
1556         if(!success){
1557             this.fireEvent("loadexception", this, o, response);
1558             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1559             return;
1560         }
1561         var result;
1562         try {
1563             result = o.reader.read(response);
1564         }catch(e){
1565             this.fireEvent("loadexception", this, o, response, e);
1566             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1567             return;
1568         }
1569         
1570         this.fireEvent("load", this, o, o.request.arg);
1571         o.request.callback.call(o.request.scope, result, o.request.arg, true);
1572     },
1573
1574     // private
1575     update : function(dataSet){
1576
1577     },
1578
1579     // private
1580     updateResponse : function(dataSet){
1581
1582     }
1583 });/*
1584  * Based on:
1585  * Ext JS Library 1.1.1
1586  * Copyright(c) 2006-2007, Ext JS, LLC.
1587  *
1588  * Originally Released Under LGPL - original licence link has changed is not relivant.
1589  *
1590  * Fork - LGPL
1591  * <script type="text/javascript">
1592  */
1593
1594 /**
1595  * @class Roo.data.ScriptTagProxy
1596  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1597  * other than the originating domain of the running page.<br><br>
1598  * <p>
1599  * <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
1600  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1601  * <p>
1602  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1603  * source code that is used as the source inside a &lt;script> tag.<br><br>
1604  * <p>
1605  * In order for the browser to process the returned data, the server must wrap the data object
1606  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1607  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1608  * depending on whether the callback name was passed:
1609  * <p>
1610  * <pre><code>
1611 boolean scriptTag = false;
1612 String cb = request.getParameter("callback");
1613 if (cb != null) {
1614     scriptTag = true;
1615     response.setContentType("text/javascript");
1616 } else {
1617     response.setContentType("application/x-json");
1618 }
1619 Writer out = response.getWriter();
1620 if (scriptTag) {
1621     out.write(cb + "(");
1622 }
1623 out.print(dataBlock.toJsonString());
1624 if (scriptTag) {
1625     out.write(");");
1626 }
1627 </pre></code>
1628  *
1629  * @constructor
1630  * @param {Object} config A configuration object.
1631  */
1632 Roo.data.ScriptTagProxy = function(config){
1633     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1634     Roo.apply(this, config);
1635     this.head = document.getElementsByTagName("head")[0];
1636 };
1637
1638 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1639
1640 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1641     /**
1642      * @cfg {String} url The URL from which to request the data object.
1643      */
1644     /**
1645      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1646      */
1647     timeout : 30000,
1648     /**
1649      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1650      * the server the name of the callback function set up by the load call to process the returned data object.
1651      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1652      * javascript output which calls this named function passing the data object as its only parameter.
1653      */
1654     callbackParam : "callback",
1655     /**
1656      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1657      * name to the request.
1658      */
1659     nocache : true,
1660
1661     /**
1662      * Load data from the configured URL, read the data object into
1663      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1664      * process that block using the passed callback.
1665      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1666      * for the request to the remote server.
1667      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1668      * object into a block of Roo.data.Records.
1669      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1670      * The function must be passed <ul>
1671      * <li>The Record block object</li>
1672      * <li>The "arg" argument from the load function</li>
1673      * <li>A boolean success indicator</li>
1674      * </ul>
1675      * @param {Object} scope The scope in which to call the callback
1676      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1677      */
1678     load : function(params, reader, callback, scope, arg){
1679         if(this.fireEvent("beforeload", this, params) !== false){
1680
1681             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1682
1683             var url = this.url;
1684             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1685             if(this.nocache){
1686                 url += "&_dc=" + (new Date().getTime());
1687             }
1688             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1689             var trans = {
1690                 id : transId,
1691                 cb : "stcCallback"+transId,
1692                 scriptId : "stcScript"+transId,
1693                 params : params,
1694                 arg : arg,
1695                 url : url,
1696                 callback : callback,
1697                 scope : scope,
1698                 reader : reader
1699             };
1700             var conn = this;
1701
1702             window[trans.cb] = function(o){
1703                 conn.handleResponse(o, trans);
1704             };
1705
1706             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1707
1708             if(this.autoAbort !== false){
1709                 this.abort();
1710             }
1711
1712             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1713
1714             var script = document.createElement("script");
1715             script.setAttribute("src", url);
1716             script.setAttribute("type", "text/javascript");
1717             script.setAttribute("id", trans.scriptId);
1718             this.head.appendChild(script);
1719
1720             this.trans = trans;
1721         }else{
1722             callback.call(scope||this, null, arg, false);
1723         }
1724     },
1725
1726     // private
1727     isLoading : function(){
1728         return this.trans ? true : false;
1729     },
1730
1731     /**
1732      * Abort the current server request.
1733      */
1734     abort : function(){
1735         if(this.isLoading()){
1736             this.destroyTrans(this.trans);
1737         }
1738     },
1739
1740     // private
1741     destroyTrans : function(trans, isLoaded){
1742         this.head.removeChild(document.getElementById(trans.scriptId));
1743         clearTimeout(trans.timeoutId);
1744         if(isLoaded){
1745             window[trans.cb] = undefined;
1746             try{
1747                 delete window[trans.cb];
1748             }catch(e){}
1749         }else{
1750             // if hasn't been loaded, wait for load to remove it to prevent script error
1751             window[trans.cb] = function(){
1752                 window[trans.cb] = undefined;
1753                 try{
1754                     delete window[trans.cb];
1755                 }catch(e){}
1756             };
1757         }
1758     },
1759
1760     // private
1761     handleResponse : function(o, trans){
1762         this.trans = false;
1763         this.destroyTrans(trans, true);
1764         var result;
1765         try {
1766             result = trans.reader.readRecords(o);
1767         }catch(e){
1768             this.fireEvent("loadexception", this, o, trans.arg, e);
1769             trans.callback.call(trans.scope||window, null, trans.arg, false);
1770             return;
1771         }
1772         this.fireEvent("load", this, o, trans.arg);
1773         trans.callback.call(trans.scope||window, result, trans.arg, true);
1774     },
1775
1776     // private
1777     handleFailure : function(trans){
1778         this.trans = false;
1779         this.destroyTrans(trans, false);
1780         this.fireEvent("loadexception", this, null, trans.arg);
1781         trans.callback.call(trans.scope||window, null, trans.arg, false);
1782     }
1783 });/*
1784  * Based on:
1785  * Ext JS Library 1.1.1
1786  * Copyright(c) 2006-2007, Ext JS, LLC.
1787  *
1788  * Originally Released Under LGPL - original licence link has changed is not relivant.
1789  *
1790  * Fork - LGPL
1791  * <script type="text/javascript">
1792  */
1793
1794 /**
1795  * @class Roo.data.JsonReader
1796  * @extends Roo.data.DataReader
1797  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1798  * based on mappings in a provided Roo.data.Record constructor.
1799  * 
1800  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1801  * in the reply previously. 
1802  * 
1803  * <p>
1804  * Example code:
1805  * <pre><code>
1806 var RecordDef = Roo.data.Record.create([
1807     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1808     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
1809 ]);
1810 var myReader = new Roo.data.JsonReader({
1811     totalProperty: "results",    // The property which contains the total dataset size (optional)
1812     root: "rows",                // The property which contains an Array of row objects
1813     id: "id"                     // The property within each row object that provides an ID for the record (optional)
1814 }, RecordDef);
1815 </code></pre>
1816  * <p>
1817  * This would consume a JSON file like this:
1818  * <pre><code>
1819 { 'results': 2, 'rows': [
1820     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1821     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1822 }
1823 </code></pre>
1824  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1825  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1826  * paged from the remote server.
1827  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1828  * @cfg {String} root name of the property which contains the Array of row objects.
1829  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1830  * @cfg {Array} fields Array of field definition objects
1831  * @constructor
1832  * Create a new JsonReader
1833  * @param {Object} meta Metadata configuration options
1834  * @param {Object} recordType Either an Array of field definition objects,
1835  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1836  */
1837 Roo.data.JsonReader = function(meta, recordType){
1838     
1839     meta = meta || {};
1840     // set some defaults:
1841     Roo.applyIf(meta, {
1842         totalProperty: 'total',
1843         successProperty : 'success',
1844         root : 'data',
1845         id : 'id'
1846     });
1847     
1848     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1849 };
1850 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1851     
1852     readerType : 'Json',
1853     
1854     /**
1855      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
1856      * Used by Store query builder to append _requestMeta to params.
1857      * 
1858      */
1859     metaFromRemote : false,
1860     /**
1861      * This method is only used by a DataProxy which has retrieved data from a remote server.
1862      * @param {Object} response The XHR object which contains the JSON data in its responseText.
1863      * @return {Object} data A data block which is used by an Roo.data.Store object as
1864      * a cache of Roo.data.Records.
1865      */
1866     read : function(response){
1867         var json = response.responseText;
1868        
1869         var o = /* eval:var:o */ eval("("+json+")");
1870         if(!o) {
1871             throw {message: "JsonReader.read: Json object not found"};
1872         }
1873         
1874         if(o.metaData){
1875             
1876             delete this.ef;
1877             this.metaFromRemote = true;
1878             this.meta = o.metaData;
1879             this.recordType = Roo.data.Record.create(o.metaData.fields);
1880             this.onMetaChange(this.meta, this.recordType, o);
1881         }
1882         return this.readRecords(o);
1883     },
1884
1885     // private function a store will implement
1886     onMetaChange : function(meta, recordType, o){
1887
1888     },
1889
1890     /**
1891          * @ignore
1892          */
1893     simpleAccess: function(obj, subsc) {
1894         return obj[subsc];
1895     },
1896
1897         /**
1898          * @ignore
1899          */
1900     getJsonAccessor: function(){
1901         var re = /[\[\.]/;
1902         return function(expr) {
1903             try {
1904                 return(re.test(expr))
1905                     ? new Function("obj", "return obj." + expr)
1906                     : function(obj){
1907                         return obj[expr];
1908                     };
1909             } catch(e){}
1910             return Roo.emptyFn;
1911         };
1912     }(),
1913
1914     /**
1915      * Create a data block containing Roo.data.Records from an XML document.
1916      * @param {Object} o An object which contains an Array of row objects in the property specified
1917      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1918      * which contains the total size of the dataset.
1919      * @return {Object} data A data block which is used by an Roo.data.Store object as
1920      * a cache of Roo.data.Records.
1921      */
1922     readRecords : function(o){
1923         /**
1924          * After any data loads, the raw JSON data is available for further custom processing.
1925          * @type Object
1926          */
1927         this.o = o;
1928         var s = this.meta, Record = this.recordType,
1929             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1930
1931 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
1932         if (!this.ef) {
1933             if(s.totalProperty) {
1934                     this.getTotal = this.getJsonAccessor(s.totalProperty);
1935                 }
1936                 if(s.successProperty) {
1937                     this.getSuccess = this.getJsonAccessor(s.successProperty);
1938                 }
1939                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1940                 if (s.id) {
1941                         var g = this.getJsonAccessor(s.id);
1942                         this.getId = function(rec) {
1943                                 var r = g(rec);  
1944                                 return (r === undefined || r === "") ? null : r;
1945                         };
1946                 } else {
1947                         this.getId = function(){return null;};
1948                 }
1949             this.ef = [];
1950             for(var jj = 0; jj < fl; jj++){
1951                 f = fi[jj];
1952                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1953                 this.ef[jj] = this.getJsonAccessor(map);
1954             }
1955         }
1956
1957         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1958         if(s.totalProperty){
1959             var vt = parseInt(this.getTotal(o), 10);
1960             if(!isNaN(vt)){
1961                 totalRecords = vt;
1962             }
1963         }
1964         if(s.successProperty){
1965             var vs = this.getSuccess(o);
1966             if(vs === false || vs === 'false'){
1967                 success = false;
1968             }
1969         }
1970         var records = [];
1971         for(var i = 0; i < c; i++){
1972                 var n = root[i];
1973             var values = {};
1974             var id = this.getId(n);
1975             for(var j = 0; j < fl; j++){
1976                 f = fi[j];
1977             var v = this.ef[j](n);
1978             if (!f.convert) {
1979                 Roo.log('missing convert for ' + f.name);
1980                 Roo.log(f);
1981                 continue;
1982             }
1983             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1984             }
1985             var record = new Record(values, id);
1986             record.json = n;
1987             records[i] = record;
1988         }
1989         return {
1990             raw : o,
1991             success : success,
1992             records : records,
1993             totalRecords : totalRecords
1994         };
1995     },
1996     // used when loading children.. @see loadDataFromChildren
1997     toLoadData: function(rec)
1998     {
1999         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2000         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2001         return { data : data, total : data.length };
2002         
2003     }
2004 });/*
2005  * Based on:
2006  * Ext JS Library 1.1.1
2007  * Copyright(c) 2006-2007, Ext JS, LLC.
2008  *
2009  * Originally Released Under LGPL - original licence link has changed is not relivant.
2010  *
2011  * Fork - LGPL
2012  * <script type="text/javascript">
2013  */
2014
2015 /**
2016  * @class Roo.data.XmlReader
2017  * @extends Roo.data.DataReader
2018  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
2019  * based on mappings in a provided Roo.data.Record constructor.<br><br>
2020  * <p>
2021  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
2022  * header in the HTTP response must be set to "text/xml".</em>
2023  * <p>
2024  * Example code:
2025  * <pre><code>
2026 var RecordDef = Roo.data.Record.create([
2027    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
2028    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
2029 ]);
2030 var myReader = new Roo.data.XmlReader({
2031    totalRecords: "results", // The element which contains the total dataset size (optional)
2032    record: "row",           // The repeated element which contains row information
2033    id: "id"                 // The element within the row that provides an ID for the record (optional)
2034 }, RecordDef);
2035 </code></pre>
2036  * <p>
2037  * This would consume an XML file like this:
2038  * <pre><code>
2039 &lt;?xml?>
2040 &lt;dataset>
2041  &lt;results>2&lt;/results>
2042  &lt;row>
2043    &lt;id>1&lt;/id>
2044    &lt;name>Bill&lt;/name>
2045    &lt;occupation>Gardener&lt;/occupation>
2046  &lt;/row>
2047  &lt;row>
2048    &lt;id>2&lt;/id>
2049    &lt;name>Ben&lt;/name>
2050    &lt;occupation>Horticulturalist&lt;/occupation>
2051  &lt;/row>
2052 &lt;/dataset>
2053 </code></pre>
2054  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2055  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2056  * paged from the remote server.
2057  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2058  * @cfg {String} success The DomQuery path to the success attribute used by forms.
2059  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2060  * a record identifier value.
2061  * @constructor
2062  * Create a new XmlReader
2063  * @param {Object} meta Metadata configuration options
2064  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
2065  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2066  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
2067  */
2068 Roo.data.XmlReader = function(meta, recordType){
2069     meta = meta || {};
2070     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2071 };
2072 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2073     
2074     readerType : 'Xml',
2075     
2076     /**
2077      * This method is only used by a DataProxy which has retrieved data from a remote server.
2078          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
2079          * to contain a method called 'responseXML' that returns an XML document object.
2080      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2081      * a cache of Roo.data.Records.
2082      */
2083     read : function(response){
2084         var doc = response.responseXML;
2085         if(!doc) {
2086             throw {message: "XmlReader.read: XML Document not available"};
2087         }
2088         return this.readRecords(doc);
2089     },
2090
2091     /**
2092      * Create a data block containing Roo.data.Records from an XML document.
2093          * @param {Object} doc A parsed XML document.
2094      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2095      * a cache of Roo.data.Records.
2096      */
2097     readRecords : function(doc){
2098         /**
2099          * After any data loads/reads, the raw XML Document is available for further custom processing.
2100          * @type XMLDocument
2101          */
2102         this.xmlData = doc;
2103         var root = doc.documentElement || doc;
2104         var q = Roo.DomQuery;
2105         var recordType = this.recordType, fields = recordType.prototype.fields;
2106         var sid = this.meta.id;
2107         var totalRecords = 0, success = true;
2108         if(this.meta.totalRecords){
2109             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2110         }
2111         
2112         if(this.meta.success){
2113             var sv = q.selectValue(this.meta.success, root, true);
2114             success = sv !== false && sv !== 'false';
2115         }
2116         var records = [];
2117         var ns = q.select(this.meta.record, root);
2118         for(var i = 0, len = ns.length; i < len; i++) {
2119                 var n = ns[i];
2120                 var values = {};
2121                 var id = sid ? q.selectValue(sid, n) : undefined;
2122                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2123                     var f = fields.items[j];
2124                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2125                     v = f.convert(v);
2126                     values[f.name] = v;
2127                 }
2128                 var record = new recordType(values, id);
2129                 record.node = n;
2130                 records[records.length] = record;
2131             }
2132
2133             return {
2134                 success : success,
2135                 records : records,
2136                 totalRecords : totalRecords || records.length
2137             };
2138     }
2139 });/*
2140  * Based on:
2141  * Ext JS Library 1.1.1
2142  * Copyright(c) 2006-2007, Ext JS, LLC.
2143  *
2144  * Originally Released Under LGPL - original licence link has changed is not relivant.
2145  *
2146  * Fork - LGPL
2147  * <script type="text/javascript">
2148  */
2149
2150 /**
2151  * @class Roo.data.ArrayReader
2152  * @extends Roo.data.DataReader
2153  * Data reader class to create an Array of Roo.data.Record objects from an Array.
2154  * Each element of that Array represents a row of data fields. The
2155  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2156  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2157  * <p>
2158  * Example code:.
2159  * <pre><code>
2160 var RecordDef = Roo.data.Record.create([
2161     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
2162     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
2163 ]);
2164 var myReader = new Roo.data.ArrayReader({
2165     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
2166 }, RecordDef);
2167 </code></pre>
2168  * <p>
2169  * This would consume an Array like this:
2170  * <pre><code>
2171 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2172   </code></pre>
2173  
2174  * @constructor
2175  * Create a new JsonReader
2176  * @param {Object} meta Metadata configuration options.
2177  * @param {Object|Array} recordType Either an Array of field definition objects
2178  * 
2179  * @cfg {Array} fields Array of field definition objects
2180  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2181  * as specified to {@link Roo.data.Record#create},
2182  * or an {@link Roo.data.Record} object
2183  *
2184  * 
2185  * created using {@link Roo.data.Record#create}.
2186  */
2187 Roo.data.ArrayReader = function(meta, recordType)
2188 {    
2189     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2190 };
2191
2192 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2193     
2194       /**
2195      * Create a data block containing Roo.data.Records from an XML document.
2196      * @param {Object} o An Array of row objects which represents the dataset.
2197      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2198      * a cache of Roo.data.Records.
2199      */
2200     readRecords : function(o)
2201     {
2202         var sid = this.meta ? this.meta.id : null;
2203         var recordType = this.recordType, fields = recordType.prototype.fields;
2204         var records = [];
2205         var root = o;
2206         for(var i = 0; i < root.length; i++){
2207             var n = root[i];
2208             var values = {};
2209             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2210             for(var j = 0, jlen = fields.length; j < jlen; j++){
2211                 var f = fields.items[j];
2212                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2213                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2214                 v = f.convert(v);
2215                 values[f.name] = v;
2216             }
2217             var record = new recordType(values, id);
2218             record.json = n;
2219             records[records.length] = record;
2220         }
2221         return {
2222             records : records,
2223             totalRecords : records.length
2224         };
2225     },
2226     // used when loading children.. @see loadDataFromChildren
2227     toLoadData: function(rec)
2228     {
2229         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2230         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2231         
2232     }
2233     
2234     
2235 });/*
2236  * Based on:
2237  * Ext JS Library 1.1.1
2238  * Copyright(c) 2006-2007, Ext JS, LLC.
2239  *
2240  * Originally Released Under LGPL - original licence link has changed is not relivant.
2241  *
2242  * Fork - LGPL
2243  * <script type="text/javascript">
2244  */
2245
2246
2247 /**
2248  * @class Roo.data.Tree
2249  * @extends Roo.util.Observable
2250  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2251  * in the tree have most standard DOM functionality.
2252  * @constructor
2253  * @param {Node} root (optional) The root node
2254  */
2255 Roo.data.Tree = function(root){
2256    this.nodeHash = {};
2257    /**
2258     * The root node for this tree
2259     * @type Node
2260     */
2261    this.root = null;
2262    if(root){
2263        this.setRootNode(root);
2264    }
2265    this.addEvents({
2266        /**
2267         * @event append
2268         * Fires when a new child node is appended to a node in this tree.
2269         * @param {Tree} tree The owner tree
2270         * @param {Node} parent The parent node
2271         * @param {Node} node The newly appended node
2272         * @param {Number} index The index of the newly appended node
2273         */
2274        "append" : true,
2275        /**
2276         * @event remove
2277         * Fires when a child node is removed from a node in this tree.
2278         * @param {Tree} tree The owner tree
2279         * @param {Node} parent The parent node
2280         * @param {Node} node The child node removed
2281         */
2282        "remove" : true,
2283        /**
2284         * @event move
2285         * Fires when a node is moved to a new location in the tree
2286         * @param {Tree} tree The owner tree
2287         * @param {Node} node The node moved
2288         * @param {Node} oldParent The old parent of this node
2289         * @param {Node} newParent The new parent of this node
2290         * @param {Number} index The index it was moved to
2291         */
2292        "move" : true,
2293        /**
2294         * @event insert
2295         * Fires when a new child node is inserted in a node in this tree.
2296         * @param {Tree} tree The owner tree
2297         * @param {Node} parent The parent node
2298         * @param {Node} node The child node inserted
2299         * @param {Node} refNode The child node the node was inserted before
2300         */
2301        "insert" : true,
2302        /**
2303         * @event beforeappend
2304         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2305         * @param {Tree} tree The owner tree
2306         * @param {Node} parent The parent node
2307         * @param {Node} node The child node to be appended
2308         */
2309        "beforeappend" : true,
2310        /**
2311         * @event beforeremove
2312         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2313         * @param {Tree} tree The owner tree
2314         * @param {Node} parent The parent node
2315         * @param {Node} node The child node to be removed
2316         */
2317        "beforeremove" : true,
2318        /**
2319         * @event beforemove
2320         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2321         * @param {Tree} tree The owner tree
2322         * @param {Node} node The node being moved
2323         * @param {Node} oldParent The parent of the node
2324         * @param {Node} newParent The new parent the node is moving to
2325         * @param {Number} index The index it is being moved to
2326         */
2327        "beforemove" : true,
2328        /**
2329         * @event beforeinsert
2330         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2331         * @param {Tree} tree The owner tree
2332         * @param {Node} parent The parent node
2333         * @param {Node} node The child node to be inserted
2334         * @param {Node} refNode The child node the node is being inserted before
2335         */
2336        "beforeinsert" : true
2337    });
2338
2339     Roo.data.Tree.superclass.constructor.call(this);
2340 };
2341
2342 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2343     pathSeparator: "/",
2344
2345     proxyNodeEvent : function(){
2346         return this.fireEvent.apply(this, arguments);
2347     },
2348
2349     /**
2350      * Returns the root node for this tree.
2351      * @return {Node}
2352      */
2353     getRootNode : function(){
2354         return this.root;
2355     },
2356
2357     /**
2358      * Sets the root node for this tree.
2359      * @param {Node} node
2360      * @return {Node}
2361      */
2362     setRootNode : function(node){
2363         this.root = node;
2364         node.ownerTree = this;
2365         node.isRoot = true;
2366         this.registerNode(node);
2367         return node;
2368     },
2369
2370     /**
2371      * Gets a node in this tree by its id.
2372      * @param {String} id
2373      * @return {Node}
2374      */
2375     getNodeById : function(id){
2376         return this.nodeHash[id];
2377     },
2378
2379     registerNode : function(node){
2380         this.nodeHash[node.id] = node;
2381     },
2382
2383     unregisterNode : function(node){
2384         delete this.nodeHash[node.id];
2385     },
2386
2387     toString : function(){
2388         return "[Tree"+(this.id?" "+this.id:"")+"]";
2389     }
2390 });
2391
2392 /**
2393  * @class Roo.data.Node
2394  * @extends Roo.util.Observable
2395  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2396  * @cfg {String} id The id for this node. If one is not specified, one is generated.
2397  * @constructor
2398  * @param {Object} attributes The attributes/config for the node
2399  */
2400 Roo.data.Node = function(attributes){
2401     /**
2402      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2403      * @type {Object}
2404      */
2405     this.attributes = attributes || {};
2406     this.leaf = this.attributes.leaf;
2407     /**
2408      * The node id. @type String
2409      */
2410     this.id = this.attributes.id;
2411     if(!this.id){
2412         this.id = Roo.id(null, "ynode-");
2413         this.attributes.id = this.id;
2414     }
2415      
2416     
2417     /**
2418      * All child nodes of this node. @type Array
2419      */
2420     this.childNodes = [];
2421     if(!this.childNodes.indexOf){ // indexOf is a must
2422         this.childNodes.indexOf = function(o){
2423             for(var i = 0, len = this.length; i < len; i++){
2424                 if(this[i] == o) {
2425                     return i;
2426                 }
2427             }
2428             return -1;
2429         };
2430     }
2431     /**
2432      * The parent node for this node. @type Node
2433      */
2434     this.parentNode = null;
2435     /**
2436      * The first direct child node of this node, or null if this node has no child nodes. @type Node
2437      */
2438     this.firstChild = null;
2439     /**
2440      * The last direct child node of this node, or null if this node has no child nodes. @type Node
2441      */
2442     this.lastChild = null;
2443     /**
2444      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2445      */
2446     this.previousSibling = null;
2447     /**
2448      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2449      */
2450     this.nextSibling = null;
2451
2452     this.addEvents({
2453        /**
2454         * @event append
2455         * Fires when a new child node is appended
2456         * @param {Tree} tree The owner tree
2457         * @param {Node} this This node
2458         * @param {Node} node The newly appended node
2459         * @param {Number} index The index of the newly appended node
2460         */
2461        "append" : true,
2462        /**
2463         * @event remove
2464         * Fires when a child node is removed
2465         * @param {Tree} tree The owner tree
2466         * @param {Node} this This node
2467         * @param {Node} node The removed node
2468         */
2469        "remove" : true,
2470        /**
2471         * @event move
2472         * Fires when this node is moved to a new location in the tree
2473         * @param {Tree} tree The owner tree
2474         * @param {Node} this This node
2475         * @param {Node} oldParent The old parent of this node
2476         * @param {Node} newParent The new parent of this node
2477         * @param {Number} index The index it was moved to
2478         */
2479        "move" : true,
2480        /**
2481         * @event insert
2482         * Fires when a new child node is inserted.
2483         * @param {Tree} tree The owner tree
2484         * @param {Node} this This node
2485         * @param {Node} node The child node inserted
2486         * @param {Node} refNode The child node the node was inserted before
2487         */
2488        "insert" : true,
2489        /**
2490         * @event beforeappend
2491         * Fires before a new child is appended, return false to cancel the append.
2492         * @param {Tree} tree The owner tree
2493         * @param {Node} this This node
2494         * @param {Node} node The child node to be appended
2495         */
2496        "beforeappend" : true,
2497        /**
2498         * @event beforeremove
2499         * Fires before a child is removed, return false to cancel the remove.
2500         * @param {Tree} tree The owner tree
2501         * @param {Node} this This node
2502         * @param {Node} node The child node to be removed
2503         */
2504        "beforeremove" : true,
2505        /**
2506         * @event beforemove
2507         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2508         * @param {Tree} tree The owner tree
2509         * @param {Node} this This node
2510         * @param {Node} oldParent The parent of this node
2511         * @param {Node} newParent The new parent this node is moving to
2512         * @param {Number} index The index it is being moved to
2513         */
2514        "beforemove" : true,
2515        /**
2516         * @event beforeinsert
2517         * Fires before a new child is inserted, return false to cancel the insert.
2518         * @param {Tree} tree The owner tree
2519         * @param {Node} this This node
2520         * @param {Node} node The child node to be inserted
2521         * @param {Node} refNode The child node the node is being inserted before
2522         */
2523        "beforeinsert" : true
2524    });
2525     this.listeners = this.attributes.listeners;
2526     Roo.data.Node.superclass.constructor.call(this);
2527 };
2528
2529 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2530     fireEvent : function(evtName){
2531         // first do standard event for this node
2532         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2533             return false;
2534         }
2535         // then bubble it up to the tree if the event wasn't cancelled
2536         var ot = this.getOwnerTree();
2537         if(ot){
2538             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2539                 return false;
2540             }
2541         }
2542         return true;
2543     },
2544
2545     /**
2546      * Returns true if this node is a leaf
2547      * @return {Boolean}
2548      */
2549     isLeaf : function(){
2550         return this.leaf === true;
2551     },
2552
2553     // private
2554     setFirstChild : function(node){
2555         this.firstChild = node;
2556     },
2557
2558     //private
2559     setLastChild : function(node){
2560         this.lastChild = node;
2561     },
2562
2563
2564     /**
2565      * Returns true if this node is the last child of its parent
2566      * @return {Boolean}
2567      */
2568     isLast : function(){
2569        return (!this.parentNode ? true : this.parentNode.lastChild == this);
2570     },
2571
2572     /**
2573      * Returns true if this node is the first child of its parent
2574      * @return {Boolean}
2575      */
2576     isFirst : function(){
2577        return (!this.parentNode ? true : this.parentNode.firstChild == this);
2578     },
2579
2580     hasChildNodes : function(){
2581         return !this.isLeaf() && this.childNodes.length > 0;
2582     },
2583
2584     /**
2585      * Insert node(s) as the last child node of this node.
2586      * @param {Node/Array} node The node or Array of nodes to append
2587      * @return {Node} The appended node if single append, or null if an array was passed
2588      */
2589     appendChild : function(node){
2590         var multi = false;
2591         if(node instanceof Array){
2592             multi = node;
2593         }else if(arguments.length > 1){
2594             multi = arguments;
2595         }
2596         
2597         // if passed an array or multiple args do them one by one
2598         if(multi){
2599             for(var i = 0, len = multi.length; i < len; i++) {
2600                 this.appendChild(multi[i]);
2601             }
2602         }else{
2603             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2604                 return false;
2605             }
2606             var index = this.childNodes.length;
2607             var oldParent = node.parentNode;
2608             // it's a move, make sure we move it cleanly
2609             if(oldParent){
2610                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2611                     return false;
2612                 }
2613                 oldParent.removeChild(node);
2614             }
2615             
2616             index = this.childNodes.length;
2617             if(index == 0){
2618                 this.setFirstChild(node);
2619             }
2620             this.childNodes.push(node);
2621             node.parentNode = this;
2622             var ps = this.childNodes[index-1];
2623             if(ps){
2624                 node.previousSibling = ps;
2625                 ps.nextSibling = node;
2626             }else{
2627                 node.previousSibling = null;
2628             }
2629             node.nextSibling = null;
2630             this.setLastChild(node);
2631             node.setOwnerTree(this.getOwnerTree());
2632             this.fireEvent("append", this.ownerTree, this, node, index);
2633             if(this.ownerTree) {
2634                 this.ownerTree.fireEvent("appendnode", this, node, index);
2635             }
2636             if(oldParent){
2637                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2638             }
2639             return node;
2640         }
2641     },
2642
2643     /**
2644      * Removes a child node from this node.
2645      * @param {Node} node The node to remove
2646      * @return {Node} The removed node
2647      */
2648     removeChild : function(node){
2649         var index = this.childNodes.indexOf(node);
2650         if(index == -1){
2651             return false;
2652         }
2653         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2654             return false;
2655         }
2656
2657         // remove it from childNodes collection
2658         this.childNodes.splice(index, 1);
2659
2660         // update siblings
2661         if(node.previousSibling){
2662             node.previousSibling.nextSibling = node.nextSibling;
2663         }
2664         if(node.nextSibling){
2665             node.nextSibling.previousSibling = node.previousSibling;
2666         }
2667
2668         // update child refs
2669         if(this.firstChild == node){
2670             this.setFirstChild(node.nextSibling);
2671         }
2672         if(this.lastChild == node){
2673             this.setLastChild(node.previousSibling);
2674         }
2675
2676         node.setOwnerTree(null);
2677         // clear any references from the node
2678         node.parentNode = null;
2679         node.previousSibling = null;
2680         node.nextSibling = null;
2681         this.fireEvent("remove", this.ownerTree, this, node);
2682         return node;
2683     },
2684
2685     /**
2686      * Inserts the first node before the second node in this nodes childNodes collection.
2687      * @param {Node} node The node to insert
2688      * @param {Node} refNode The node to insert before (if null the node is appended)
2689      * @return {Node} The inserted node
2690      */
2691     insertBefore : function(node, refNode){
2692         if(!refNode){ // like standard Dom, refNode can be null for append
2693             return this.appendChild(node);
2694         }
2695         // nothing to do
2696         if(node == refNode){
2697             return false;
2698         }
2699
2700         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2701             return false;
2702         }
2703         var index = this.childNodes.indexOf(refNode);
2704         var oldParent = node.parentNode;
2705         var refIndex = index;
2706
2707         // when moving internally, indexes will change after remove
2708         if(oldParent == this && this.childNodes.indexOf(node) < index){
2709             refIndex--;
2710         }
2711
2712         // it's a move, make sure we move it cleanly
2713         if(oldParent){
2714             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2715                 return false;
2716             }
2717             oldParent.removeChild(node);
2718         }
2719         if(refIndex == 0){
2720             this.setFirstChild(node);
2721         }
2722         this.childNodes.splice(refIndex, 0, node);
2723         node.parentNode = this;
2724         var ps = this.childNodes[refIndex-1];
2725         if(ps){
2726             node.previousSibling = ps;
2727             ps.nextSibling = node;
2728         }else{
2729             node.previousSibling = null;
2730         }
2731         node.nextSibling = refNode;
2732         refNode.previousSibling = node;
2733         node.setOwnerTree(this.getOwnerTree());
2734         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2735         if(oldParent){
2736             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2737         }
2738         return node;
2739     },
2740
2741     /**
2742      * Returns the child node at the specified index.
2743      * @param {Number} index
2744      * @return {Node}
2745      */
2746     item : function(index){
2747         return this.childNodes[index];
2748     },
2749
2750     /**
2751      * Replaces one child node in this node with another.
2752      * @param {Node} newChild The replacement node
2753      * @param {Node} oldChild The node to replace
2754      * @return {Node} The replaced node
2755      */
2756     replaceChild : function(newChild, oldChild){
2757         this.insertBefore(newChild, oldChild);
2758         this.removeChild(oldChild);
2759         return oldChild;
2760     },
2761
2762     /**
2763      * Returns the index of a child node
2764      * @param {Node} node
2765      * @return {Number} The index of the node or -1 if it was not found
2766      */
2767     indexOf : function(child){
2768         return this.childNodes.indexOf(child);
2769     },
2770
2771     /**
2772      * Returns the tree this node is in.
2773      * @return {Tree}
2774      */
2775     getOwnerTree : function(){
2776         // if it doesn't have one, look for one
2777         if(!this.ownerTree){
2778             var p = this;
2779             while(p){
2780                 if(p.ownerTree){
2781                     this.ownerTree = p.ownerTree;
2782                     break;
2783                 }
2784                 p = p.parentNode;
2785             }
2786         }
2787         return this.ownerTree;
2788     },
2789
2790     /**
2791      * Returns depth of this node (the root node has a depth of 0)
2792      * @return {Number}
2793      */
2794     getDepth : function(){
2795         var depth = 0;
2796         var p = this;
2797         while(p.parentNode){
2798             ++depth;
2799             p = p.parentNode;
2800         }
2801         return depth;
2802     },
2803
2804     // private
2805     setOwnerTree : function(tree){
2806         // if it's move, we need to update everyone
2807         if(tree != this.ownerTree){
2808             if(this.ownerTree){
2809                 this.ownerTree.unregisterNode(this);
2810             }
2811             this.ownerTree = tree;
2812             var cs = this.childNodes;
2813             for(var i = 0, len = cs.length; i < len; i++) {
2814                 cs[i].setOwnerTree(tree);
2815             }
2816             if(tree){
2817                 tree.registerNode(this);
2818             }
2819         }
2820     },
2821
2822     /**
2823      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2824      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2825      * @return {String} The path
2826      */
2827     getPath : function(attr){
2828         attr = attr || "id";
2829         var p = this.parentNode;
2830         var b = [this.attributes[attr]];
2831         while(p){
2832             b.unshift(p.attributes[attr]);
2833             p = p.parentNode;
2834         }
2835         var sep = this.getOwnerTree().pathSeparator;
2836         return sep + b.join(sep);
2837     },
2838
2839     /**
2840      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2841      * function call will be the scope provided or the current node. The arguments to the function
2842      * will be the args provided or the current node. If the function returns false at any point,
2843      * the bubble is stopped.
2844      * @param {Function} fn The function to call
2845      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2846      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2847      */
2848     bubble : function(fn, scope, args){
2849         var p = this;
2850         while(p){
2851             if(fn.call(scope || p, args || p) === false){
2852                 break;
2853             }
2854             p = p.parentNode;
2855         }
2856     },
2857
2858     /**
2859      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2860      * function call will be the scope provided or the current node. The arguments to the function
2861      * will be the args provided or the current node. If the function returns false at any point,
2862      * the cascade is stopped on that branch.
2863      * @param {Function} fn The function to call
2864      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2865      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2866      */
2867     cascade : function(fn, scope, args){
2868         if(fn.call(scope || this, args || this) !== false){
2869             var cs = this.childNodes;
2870             for(var i = 0, len = cs.length; i < len; i++) {
2871                 cs[i].cascade(fn, scope, args);
2872             }
2873         }
2874     },
2875
2876     /**
2877      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2878      * function call will be the scope provided or the current node. The arguments to the function
2879      * will be the args provided or the current node. If the function returns false at any point,
2880      * the iteration stops.
2881      * @param {Function} fn The function to call
2882      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2883      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2884      */
2885     eachChild : function(fn, scope, args){
2886         var cs = this.childNodes;
2887         for(var i = 0, len = cs.length; i < len; i++) {
2888                 if(fn.call(scope || this, args || cs[i]) === false){
2889                     break;
2890                 }
2891         }
2892     },
2893
2894     /**
2895      * Finds the first child that has the attribute with the specified value.
2896      * @param {String} attribute The attribute name
2897      * @param {Mixed} value The value to search for
2898      * @return {Node} The found child or null if none was found
2899      */
2900     findChild : function(attribute, value){
2901         var cs = this.childNodes;
2902         for(var i = 0, len = cs.length; i < len; i++) {
2903                 if(cs[i].attributes[attribute] == value){
2904                     return cs[i];
2905                 }
2906         }
2907         return null;
2908     },
2909
2910     /**
2911      * Finds the first child by a custom function. The child matches if the function passed
2912      * returns true.
2913      * @param {Function} fn
2914      * @param {Object} scope (optional)
2915      * @return {Node} The found child or null if none was found
2916      */
2917     findChildBy : function(fn, scope){
2918         var cs = this.childNodes;
2919         for(var i = 0, len = cs.length; i < len; i++) {
2920                 if(fn.call(scope||cs[i], cs[i]) === true){
2921                     return cs[i];
2922                 }
2923         }
2924         return null;
2925     },
2926
2927     /**
2928      * Sorts this nodes children using the supplied sort function
2929      * @param {Function} fn
2930      * @param {Object} scope (optional)
2931      */
2932     sort : function(fn, scope){
2933         var cs = this.childNodes;
2934         var len = cs.length;
2935         if(len > 0){
2936             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2937             cs.sort(sortFn);
2938             for(var i = 0; i < len; i++){
2939                 var n = cs[i];
2940                 n.previousSibling = cs[i-1];
2941                 n.nextSibling = cs[i+1];
2942                 if(i == 0){
2943                     this.setFirstChild(n);
2944                 }
2945                 if(i == len-1){
2946                     this.setLastChild(n);
2947                 }
2948             }
2949         }
2950     },
2951
2952     /**
2953      * Returns true if this node is an ancestor (at any point) of the passed node.
2954      * @param {Node} node
2955      * @return {Boolean}
2956      */
2957     contains : function(node){
2958         return node.isAncestor(this);
2959     },
2960
2961     /**
2962      * Returns true if the passed node is an ancestor (at any point) of this node.
2963      * @param {Node} node
2964      * @return {Boolean}
2965      */
2966     isAncestor : function(node){
2967         var p = this.parentNode;
2968         while(p){
2969             if(p == node){
2970                 return true;
2971             }
2972             p = p.parentNode;
2973         }
2974         return false;
2975     },
2976
2977     toString : function(){
2978         return "[Node"+(this.id?" "+this.id:"")+"]";
2979     }
2980 });/*
2981  * Based on:
2982  * Ext JS Library 1.1.1
2983  * Copyright(c) 2006-2007, Ext JS, LLC.
2984  *
2985  * Originally Released Under LGPL - original licence link has changed is not relivant.
2986  *
2987  * Fork - LGPL
2988  * <script type="text/javascript">
2989  */
2990
2991
2992 /**
2993  * @class Roo.Shadow
2994  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
2995  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
2996  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
2997  * @constructor
2998  * Create a new Shadow
2999  * @param {Object} config The config object
3000  */
3001 Roo.Shadow = function(config){
3002     Roo.apply(this, config);
3003     if(typeof this.mode != "string"){
3004         this.mode = this.defaultMode;
3005     }
3006     var o = this.offset, a = {h: 0};
3007     var rad = Math.floor(this.offset/2);
3008     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3009         case "drop":
3010             a.w = 0;
3011             a.l = a.t = o;
3012             a.t -= 1;
3013             if(Roo.isIE){
3014                 a.l -= this.offset + rad;
3015                 a.t -= this.offset + rad;
3016                 a.w -= rad;
3017                 a.h -= rad;
3018                 a.t += 1;
3019             }
3020         break;
3021         case "sides":
3022             a.w = (o*2);
3023             a.l = -o;
3024             a.t = o-1;
3025             if(Roo.isIE){
3026                 a.l -= (this.offset - rad);
3027                 a.t -= this.offset + rad;
3028                 a.l += 1;
3029                 a.w -= (this.offset - rad)*2;
3030                 a.w -= rad + 1;
3031                 a.h -= 1;
3032             }
3033         break;
3034         case "frame":
3035             a.w = a.h = (o*2);
3036             a.l = a.t = -o;
3037             a.t += 1;
3038             a.h -= 2;
3039             if(Roo.isIE){
3040                 a.l -= (this.offset - rad);
3041                 a.t -= (this.offset - rad);
3042                 a.l += 1;
3043                 a.w -= (this.offset + rad + 1);
3044                 a.h -= (this.offset + rad);
3045                 a.h += 1;
3046             }
3047         break;
3048     };
3049
3050     this.adjusts = a;
3051 };
3052
3053 Roo.Shadow.prototype = {
3054     /**
3055      * @cfg {String} mode
3056      * The shadow display mode.  Supports the following options:<br />
3057      * sides: Shadow displays on both sides and bottom only<br />
3058      * frame: Shadow displays equally on all four sides<br />
3059      * drop: Traditional bottom-right drop shadow (default)
3060      */
3061     mode: false,
3062     /**
3063      * @cfg {String} offset
3064      * The number of pixels to offset the shadow from the element (defaults to 4)
3065      */
3066     offset: 4,
3067
3068     // private
3069     defaultMode: "drop",
3070
3071     /**
3072      * Displays the shadow under the target element
3073      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3074      */
3075     show : function(target){
3076         target = Roo.get(target);
3077         if(!this.el){
3078             this.el = Roo.Shadow.Pool.pull();
3079             if(this.el.dom.nextSibling != target.dom){
3080                 this.el.insertBefore(target);
3081             }
3082         }
3083         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3084         if(Roo.isIE){
3085             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3086         }
3087         this.realign(
3088             target.getLeft(true),
3089             target.getTop(true),
3090             target.getWidth(),
3091             target.getHeight()
3092         );
3093         this.el.dom.style.display = "block";
3094     },
3095
3096     /**
3097      * Returns true if the shadow is visible, else false
3098      */
3099     isVisible : function(){
3100         return this.el ? true : false;  
3101     },
3102
3103     /**
3104      * Direct alignment when values are already available. Show must be called at least once before
3105      * calling this method to ensure it is initialized.
3106      * @param {Number} left The target element left position
3107      * @param {Number} top The target element top position
3108      * @param {Number} width The target element width
3109      * @param {Number} height The target element height
3110      */
3111     realign : function(l, t, w, h){
3112         if(!this.el){
3113             return;
3114         }
3115         var a = this.adjusts, d = this.el.dom, s = d.style;
3116         var iea = 0;
3117         s.left = (l+a.l)+"px";
3118         s.top = (t+a.t)+"px";
3119         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3120  
3121         if(s.width != sws || s.height != shs){
3122             s.width = sws;
3123             s.height = shs;
3124             if(!Roo.isIE){
3125                 var cn = d.childNodes;
3126                 var sww = Math.max(0, (sw-12))+"px";
3127                 cn[0].childNodes[1].style.width = sww;
3128                 cn[1].childNodes[1].style.width = sww;
3129                 cn[2].childNodes[1].style.width = sww;
3130                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3131             }
3132         }
3133     },
3134
3135     /**
3136      * Hides this shadow
3137      */
3138     hide : function(){
3139         if(this.el){
3140             this.el.dom.style.display = "none";
3141             Roo.Shadow.Pool.push(this.el);
3142             delete this.el;
3143         }
3144     },
3145
3146     /**
3147      * Adjust the z-index of this shadow
3148      * @param {Number} zindex The new z-index
3149      */
3150     setZIndex : function(z){
3151         this.zIndex = z;
3152         if(this.el){
3153             this.el.setStyle("z-index", z);
3154         }
3155     }
3156 };
3157
3158 // Private utility class that manages the internal Shadow cache
3159 Roo.Shadow.Pool = function(){
3160     var p = [];
3161     var markup = Roo.isIE ?
3162                  '<div class="x-ie-shadow"></div>' :
3163                  '<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>';
3164     return {
3165         pull : function(){
3166             var sh = p.shift();
3167             if(!sh){
3168                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3169                 sh.autoBoxAdjust = false;
3170             }
3171             return sh;
3172         },
3173
3174         push : function(sh){
3175             p.push(sh);
3176         }
3177     };
3178 }();/*
3179  * Based on:
3180  * Ext JS Library 1.1.1
3181  * Copyright(c) 2006-2007, Ext JS, LLC.
3182  *
3183  * Originally Released Under LGPL - original licence link has changed is not relivant.
3184  *
3185  * Fork - LGPL
3186  * <script type="text/javascript">
3187  */
3188
3189
3190 /**
3191  * @class Roo.SplitBar
3192  * @extends Roo.util.Observable
3193  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3194  * <br><br>
3195  * Usage:
3196  * <pre><code>
3197 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3198                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3199 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3200 split.minSize = 100;
3201 split.maxSize = 600;
3202 split.animate = true;
3203 split.on('moved', splitterMoved);
3204 </code></pre>
3205  * @constructor
3206  * Create a new SplitBar
3207  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3208  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3209  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3210  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3211                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3212                         position of the SplitBar).
3213  */
3214 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3215     
3216     /** @private */
3217     this.el = Roo.get(dragElement, true);
3218     this.el.dom.unselectable = "on";
3219     /** @private */
3220     this.resizingEl = Roo.get(resizingElement, true);
3221
3222     /**
3223      * @private
3224      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3225      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3226      * @type Number
3227      */
3228     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3229     
3230     /**
3231      * The minimum size of the resizing element. (Defaults to 0)
3232      * @type Number
3233      */
3234     this.minSize = 0;
3235     
3236     /**
3237      * The maximum size of the resizing element. (Defaults to 2000)
3238      * @type Number
3239      */
3240     this.maxSize = 2000;
3241     
3242     /**
3243      * Whether to animate the transition to the new size
3244      * @type Boolean
3245      */
3246     this.animate = false;
3247     
3248     /**
3249      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3250      * @type Boolean
3251      */
3252     this.useShim = false;
3253     
3254     /** @private */
3255     this.shim = null;
3256     
3257     if(!existingProxy){
3258         /** @private */
3259         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3260     }else{
3261         this.proxy = Roo.get(existingProxy).dom;
3262     }
3263     /** @private */
3264     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3265     
3266     /** @private */
3267     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3268     
3269     /** @private */
3270     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3271     
3272     /** @private */
3273     this.dragSpecs = {};
3274     
3275     /**
3276      * @private The adapter to use to positon and resize elements
3277      */
3278     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3279     this.adapter.init(this);
3280     
3281     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3282         /** @private */
3283         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3284         this.el.addClass("x-splitbar-h");
3285     }else{
3286         /** @private */
3287         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3288         this.el.addClass("x-splitbar-v");
3289     }
3290     
3291     this.addEvents({
3292         /**
3293          * @event resize
3294          * Fires when the splitter is moved (alias for {@link #event-moved})
3295          * @param {Roo.SplitBar} this
3296          * @param {Number} newSize the new width or height
3297          */
3298         "resize" : true,
3299         /**
3300          * @event moved
3301          * Fires when the splitter is moved
3302          * @param {Roo.SplitBar} this
3303          * @param {Number} newSize the new width or height
3304          */
3305         "moved" : true,
3306         /**
3307          * @event beforeresize
3308          * Fires before the splitter is dragged
3309          * @param {Roo.SplitBar} this
3310          */
3311         "beforeresize" : true,
3312
3313         "beforeapply" : true
3314     });
3315
3316     Roo.util.Observable.call(this);
3317 };
3318
3319 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3320     onStartProxyDrag : function(x, y){
3321         this.fireEvent("beforeresize", this);
3322         if(!this.overlay){
3323             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3324             o.unselectable();
3325             o.enableDisplayMode("block");
3326             // all splitbars share the same overlay
3327             Roo.SplitBar.prototype.overlay = o;
3328         }
3329         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3330         this.overlay.show();
3331         Roo.get(this.proxy).setDisplayed("block");
3332         var size = this.adapter.getElementSize(this);
3333         this.activeMinSize = this.getMinimumSize();;
3334         this.activeMaxSize = this.getMaximumSize();;
3335         var c1 = size - this.activeMinSize;
3336         var c2 = Math.max(this.activeMaxSize - size, 0);
3337         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3338             this.dd.resetConstraints();
3339             this.dd.setXConstraint(
3340                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3341                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3342             );
3343             this.dd.setYConstraint(0, 0);
3344         }else{
3345             this.dd.resetConstraints();
3346             this.dd.setXConstraint(0, 0);
3347             this.dd.setYConstraint(
3348                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3349                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3350             );
3351          }
3352         this.dragSpecs.startSize = size;
3353         this.dragSpecs.startPoint = [x, y];
3354         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3355     },
3356     
3357     /** 
3358      * @private Called after the drag operation by the DDProxy
3359      */
3360     onEndProxyDrag : function(e){
3361         Roo.get(this.proxy).setDisplayed(false);
3362         var endPoint = Roo.lib.Event.getXY(e);
3363         if(this.overlay){
3364             this.overlay.hide();
3365         }
3366         var newSize;
3367         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3368             newSize = this.dragSpecs.startSize + 
3369                 (this.placement == Roo.SplitBar.LEFT ?
3370                     endPoint[0] - this.dragSpecs.startPoint[0] :
3371                     this.dragSpecs.startPoint[0] - endPoint[0]
3372                 );
3373         }else{
3374             newSize = this.dragSpecs.startSize + 
3375                 (this.placement == Roo.SplitBar.TOP ?
3376                     endPoint[1] - this.dragSpecs.startPoint[1] :
3377                     this.dragSpecs.startPoint[1] - endPoint[1]
3378                 );
3379         }
3380         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3381         if(newSize != this.dragSpecs.startSize){
3382             if(this.fireEvent('beforeapply', this, newSize) !== false){
3383                 this.adapter.setElementSize(this, newSize);
3384                 this.fireEvent("moved", this, newSize);
3385                 this.fireEvent("resize", this, newSize);
3386             }
3387         }
3388     },
3389     
3390     /**
3391      * Get the adapter this SplitBar uses
3392      * @return The adapter object
3393      */
3394     getAdapter : function(){
3395         return this.adapter;
3396     },
3397     
3398     /**
3399      * Set the adapter this SplitBar uses
3400      * @param {Object} adapter A SplitBar adapter object
3401      */
3402     setAdapter : function(adapter){
3403         this.adapter = adapter;
3404         this.adapter.init(this);
3405     },
3406     
3407     /**
3408      * Gets the minimum size for the resizing element
3409      * @return {Number} The minimum size
3410      */
3411     getMinimumSize : function(){
3412         return this.minSize;
3413     },
3414     
3415     /**
3416      * Sets the minimum size for the resizing element
3417      * @param {Number} minSize The minimum size
3418      */
3419     setMinimumSize : function(minSize){
3420         this.minSize = minSize;
3421     },
3422     
3423     /**
3424      * Gets the maximum size for the resizing element
3425      * @return {Number} The maximum size
3426      */
3427     getMaximumSize : function(){
3428         return this.maxSize;
3429     },
3430     
3431     /**
3432      * Sets the maximum size for the resizing element
3433      * @param {Number} maxSize The maximum size
3434      */
3435     setMaximumSize : function(maxSize){
3436         this.maxSize = maxSize;
3437     },
3438     
3439     /**
3440      * Sets the initialize size for the resizing element
3441      * @param {Number} size The initial size
3442      */
3443     setCurrentSize : function(size){
3444         var oldAnimate = this.animate;
3445         this.animate = false;
3446         this.adapter.setElementSize(this, size);
3447         this.animate = oldAnimate;
3448     },
3449     
3450     /**
3451      * Destroy this splitbar. 
3452      * @param {Boolean} removeEl True to remove the element
3453      */
3454     destroy : function(removeEl){
3455         if(this.shim){
3456             this.shim.remove();
3457         }
3458         this.dd.unreg();
3459         this.proxy.parentNode.removeChild(this.proxy);
3460         if(removeEl){
3461             this.el.remove();
3462         }
3463     }
3464 });
3465
3466 /**
3467  * @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.
3468  */
3469 Roo.SplitBar.createProxy = function(dir){
3470     var proxy = new Roo.Element(document.createElement("div"));
3471     proxy.unselectable();
3472     var cls = 'x-splitbar-proxy';
3473     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3474     document.body.appendChild(proxy.dom);
3475     return proxy.dom;
3476 };
3477
3478 /** 
3479  * @class Roo.SplitBar.BasicLayoutAdapter
3480  * Default Adapter. It assumes the splitter and resizing element are not positioned
3481  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3482  */
3483 Roo.SplitBar.BasicLayoutAdapter = function(){
3484 };
3485
3486 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3487     // do nothing for now
3488     init : function(s){
3489     
3490     },
3491     /**
3492      * Called before drag operations to get the current size of the resizing element. 
3493      * @param {Roo.SplitBar} s The SplitBar using this adapter
3494      */
3495      getElementSize : function(s){
3496         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3497             return s.resizingEl.getWidth();
3498         }else{
3499             return s.resizingEl.getHeight();
3500         }
3501     },
3502     
3503     /**
3504      * Called after drag operations to set the size of the resizing element.
3505      * @param {Roo.SplitBar} s The SplitBar using this adapter
3506      * @param {Number} newSize The new size to set
3507      * @param {Function} onComplete A function to be invoked when resizing is complete
3508      */
3509     setElementSize : function(s, newSize, onComplete){
3510         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3511             if(!s.animate){
3512                 s.resizingEl.setWidth(newSize);
3513                 if(onComplete){
3514                     onComplete(s, newSize);
3515                 }
3516             }else{
3517                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3518             }
3519         }else{
3520             
3521             if(!s.animate){
3522                 s.resizingEl.setHeight(newSize);
3523                 if(onComplete){
3524                     onComplete(s, newSize);
3525                 }
3526             }else{
3527                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3528             }
3529         }
3530     }
3531 };
3532
3533 /** 
3534  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3535  * @extends Roo.SplitBar.BasicLayoutAdapter
3536  * Adapter that  moves the splitter element to align with the resized sizing element. 
3537  * Used with an absolute positioned SplitBar.
3538  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3539  * document.body, make sure you assign an id to the body element.
3540  */
3541 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3542     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3543     this.container = Roo.get(container);
3544 };
3545
3546 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3547     init : function(s){
3548         this.basic.init(s);
3549     },
3550     
3551     getElementSize : function(s){
3552         return this.basic.getElementSize(s);
3553     },
3554     
3555     setElementSize : function(s, newSize, onComplete){
3556         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3557     },
3558     
3559     moveSplitter : function(s){
3560         var yes = Roo.SplitBar;
3561         switch(s.placement){
3562             case yes.LEFT:
3563                 s.el.setX(s.resizingEl.getRight());
3564                 break;
3565             case yes.RIGHT:
3566                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3567                 break;
3568             case yes.TOP:
3569                 s.el.setY(s.resizingEl.getBottom());
3570                 break;
3571             case yes.BOTTOM:
3572                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3573                 break;
3574         }
3575     }
3576 };
3577
3578 /**
3579  * Orientation constant - Create a vertical SplitBar
3580  * @static
3581  * @type Number
3582  */
3583 Roo.SplitBar.VERTICAL = 1;
3584
3585 /**
3586  * Orientation constant - Create a horizontal SplitBar
3587  * @static
3588  * @type Number
3589  */
3590 Roo.SplitBar.HORIZONTAL = 2;
3591
3592 /**
3593  * Placement constant - The resizing element is to the left of the splitter element
3594  * @static
3595  * @type Number
3596  */
3597 Roo.SplitBar.LEFT = 1;
3598
3599 /**
3600  * Placement constant - The resizing element is to the right of the splitter element
3601  * @static
3602  * @type Number
3603  */
3604 Roo.SplitBar.RIGHT = 2;
3605
3606 /**
3607  * Placement constant - The resizing element is positioned above the splitter element
3608  * @static
3609  * @type Number
3610  */
3611 Roo.SplitBar.TOP = 3;
3612
3613 /**
3614  * Placement constant - The resizing element is positioned under splitter element
3615  * @static
3616  * @type Number
3617  */
3618 Roo.SplitBar.BOTTOM = 4;
3619 /*
3620  * Based on:
3621  * Ext JS Library 1.1.1
3622  * Copyright(c) 2006-2007, Ext JS, LLC.
3623  *
3624  * Originally Released Under LGPL - original licence link has changed is not relivant.
3625  *
3626  * Fork - LGPL
3627  * <script type="text/javascript">
3628  */
3629
3630 /**
3631  * @class Roo.View
3632  * @extends Roo.util.Observable
3633  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
3634  * This class also supports single and multi selection modes. <br>
3635  * Create a data model bound view:
3636  <pre><code>
3637  var store = new Roo.data.Store(...);
3638
3639  var view = new Roo.View({
3640     el : "my-element",
3641     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
3642  
3643     singleSelect: true,
3644     selectedClass: "ydataview-selected",
3645     store: store
3646  });
3647
3648  // listen for node click?
3649  view.on("click", function(vw, index, node, e){
3650  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
3651  });
3652
3653  // load XML data
3654  dataModel.load("foobar.xml");
3655  </code></pre>
3656  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
3657  * <br><br>
3658  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
3659  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
3660  * 
3661  * Note: old style constructor is still suported (container, template, config)
3662  * 
3663  * @constructor
3664  * Create a new View
3665  * @param {Object} config The config object
3666  * 
3667  */
3668 Roo.View = function(config, depreciated_tpl, depreciated_config){
3669     
3670     this.parent = false;
3671     
3672     if (typeof(depreciated_tpl) == 'undefined') {
3673         // new way.. - universal constructor.
3674         Roo.apply(this, config);
3675         this.el  = Roo.get(this.el);
3676     } else {
3677         // old format..
3678         this.el  = Roo.get(config);
3679         this.tpl = depreciated_tpl;
3680         Roo.apply(this, depreciated_config);
3681     }
3682     this.wrapEl  = this.el.wrap().wrap();
3683     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
3684     
3685     
3686     if(typeof(this.tpl) == "string"){
3687         this.tpl = new Roo.Template(this.tpl);
3688     } else {
3689         // support xtype ctors..
3690         this.tpl = new Roo.factory(this.tpl, Roo);
3691     }
3692     
3693     
3694     this.tpl.compile();
3695     
3696     /** @private */
3697     this.addEvents({
3698         /**
3699          * @event beforeclick
3700          * Fires before a click is processed. Returns false to cancel the default action.
3701          * @param {Roo.View} this
3702          * @param {Number} index The index of the target node
3703          * @param {HTMLElement} node The target node
3704          * @param {Roo.EventObject} e The raw event object
3705          */
3706             "beforeclick" : true,
3707         /**
3708          * @event click
3709          * Fires when a template node is clicked.
3710          * @param {Roo.View} this
3711          * @param {Number} index The index of the target node
3712          * @param {HTMLElement} node The target node
3713          * @param {Roo.EventObject} e The raw event object
3714          */
3715             "click" : true,
3716         /**
3717          * @event dblclick
3718          * Fires when a template node is double clicked.
3719          * @param {Roo.View} this
3720          * @param {Number} index The index of the target node
3721          * @param {HTMLElement} node The target node
3722          * @param {Roo.EventObject} e The raw event object
3723          */
3724             "dblclick" : true,
3725         /**
3726          * @event contextmenu
3727          * Fires when a template node is right clicked.
3728          * @param {Roo.View} this
3729          * @param {Number} index The index of the target node
3730          * @param {HTMLElement} node The target node
3731          * @param {Roo.EventObject} e The raw event object
3732          */
3733             "contextmenu" : true,
3734         /**
3735          * @event selectionchange
3736          * Fires when the selected nodes change.
3737          * @param {Roo.View} this
3738          * @param {Array} selections Array of the selected nodes
3739          */
3740             "selectionchange" : true,
3741     
3742         /**
3743          * @event beforeselect
3744          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
3745          * @param {Roo.View} this
3746          * @param {HTMLElement} node The node to be selected
3747          * @param {Array} selections Array of currently selected nodes
3748          */
3749             "beforeselect" : true,
3750         /**
3751          * @event preparedata
3752          * Fires on every row to render, to allow you to change the data.
3753          * @param {Roo.View} this
3754          * @param {Object} data to be rendered (change this)
3755          */
3756           "preparedata" : true
3757           
3758           
3759         });
3760
3761
3762
3763     this.el.on({
3764         "click": this.onClick,
3765         "dblclick": this.onDblClick,
3766         "contextmenu": this.onContextMenu,
3767         scope:this
3768     });
3769
3770     this.selections = [];
3771     this.nodes = [];
3772     this.cmp = new Roo.CompositeElementLite([]);
3773     if(this.store){
3774         this.store = Roo.factory(this.store, Roo.data);
3775         this.setStore(this.store, true);
3776     }
3777     
3778     if ( this.footer && this.footer.xtype) {
3779            
3780          var fctr = this.wrapEl.appendChild(document.createElement("div"));
3781         
3782         this.footer.dataSource = this.store;
3783         this.footer.container = fctr;
3784         this.footer = Roo.factory(this.footer, Roo);
3785         fctr.insertFirst(this.el);
3786         
3787         // this is a bit insane - as the paging toolbar seems to detach the el..
3788 //        dom.parentNode.parentNode.parentNode
3789          // they get detached?
3790     }
3791     
3792     
3793     Roo.View.superclass.constructor.call(this);
3794     
3795     
3796 };
3797
3798 Roo.extend(Roo.View, Roo.util.Observable, {
3799     
3800      /**
3801      * @cfg {Roo.data.Store} store Data store to load data from.
3802      */
3803     store : false,
3804     
3805     /**
3806      * @cfg {String|Roo.Element} el The container element.
3807      */
3808     el : '',
3809     
3810     /**
3811      * @cfg {String|Roo.Template} tpl The template used by this View 
3812      */
3813     tpl : false,
3814     /**
3815      * @cfg {String} dataName the named area of the template to use as the data area
3816      *                          Works with domtemplates roo-name="name"
3817      */
3818     dataName: false,
3819     /**
3820      * @cfg {String} selectedClass The css class to add to selected nodes
3821      */
3822     selectedClass : "x-view-selected",
3823      /**
3824      * @cfg {String} emptyText The empty text to show when nothing is loaded.
3825      */
3826     emptyText : "",
3827     
3828     /**
3829      * @cfg {String} text to display on mask (default Loading)
3830      */
3831     mask : false,
3832     /**
3833      * @cfg {Boolean} multiSelect Allow multiple selection
3834      */
3835     multiSelect : false,
3836     /**
3837      * @cfg {Boolean} singleSelect Allow single selection
3838      */
3839     singleSelect:  false,
3840     
3841     /**
3842      * @cfg {Boolean} toggleSelect - selecting 
3843      */
3844     toggleSelect : false,
3845     
3846     /**
3847      * @cfg {Boolean} tickable - selecting 
3848      */
3849     tickable : false,
3850     
3851     /**
3852      * Returns the element this view is bound to.
3853      * @return {Roo.Element}
3854      */
3855     getEl : function(){
3856         return this.wrapEl;
3857     },
3858     
3859     
3860
3861     /**
3862      * Refreshes the view. - called by datachanged on the store. - do not call directly.
3863      */
3864     refresh : function(){
3865         //Roo.log('refresh');
3866         var t = this.tpl;
3867         
3868         // if we are using something like 'domtemplate', then
3869         // the what gets used is:
3870         // t.applySubtemplate(NAME, data, wrapping data..)
3871         // the outer template then get' applied with
3872         //     the store 'extra data'
3873         // and the body get's added to the
3874         //      roo-name="data" node?
3875         //      <span class='roo-tpl-{name}'></span> ?????
3876         
3877         
3878         
3879         this.clearSelections();
3880         this.el.update("");
3881         var html = [];
3882         var records = this.store.getRange();
3883         if(records.length < 1) {
3884             
3885             // is this valid??  = should it render a template??
3886             
3887             this.el.update(this.emptyText);
3888             return;
3889         }
3890         var el = this.el;
3891         if (this.dataName) {
3892             this.el.update(t.apply(this.store.meta)); //????
3893             el = this.el.child('.roo-tpl-' + this.dataName);
3894         }
3895         
3896         for(var i = 0, len = records.length; i < len; i++){
3897             var data = this.prepareData(records[i].data, i, records[i]);
3898             this.fireEvent("preparedata", this, data, i, records[i]);
3899             
3900             var d = Roo.apply({}, data);
3901             
3902             if(this.tickable){
3903                 Roo.apply(d, {'roo-id' : Roo.id()});
3904                 
3905                 var _this = this;
3906             
3907                 Roo.each(this.parent.item, function(item){
3908                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
3909                         return;
3910                     }
3911                     Roo.apply(d, {'roo-data-checked' : 'checked'});
3912                 });
3913             }
3914             
3915             html[html.length] = Roo.util.Format.trim(
3916                 this.dataName ?
3917                     t.applySubtemplate(this.dataName, d, this.store.meta) :
3918                     t.apply(d)
3919             );
3920         }
3921         
3922         
3923         
3924         el.update(html.join(""));
3925         this.nodes = el.dom.childNodes;
3926         this.updateIndexes(0);
3927     },
3928     
3929
3930     /**
3931      * Function to override to reformat the data that is sent to
3932      * the template for each node.
3933      * DEPRICATED - use the preparedata event handler.
3934      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
3935      * a JSON object for an UpdateManager bound view).
3936      */
3937     prepareData : function(data, index, record)
3938     {
3939         this.fireEvent("preparedata", this, data, index, record);
3940         return data;
3941     },
3942
3943     onUpdate : function(ds, record){
3944         // Roo.log('on update');   
3945         this.clearSelections();
3946         var index = this.store.indexOf(record);
3947         var n = this.nodes[index];
3948         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
3949         n.parentNode.removeChild(n);
3950         this.updateIndexes(index, index);
3951     },
3952
3953     
3954     
3955 // --------- FIXME     
3956     onAdd : function(ds, records, index)
3957     {
3958         //Roo.log(['on Add', ds, records, index] );        
3959         this.clearSelections();
3960         if(this.nodes.length == 0){
3961             this.refresh();
3962             return;
3963         }
3964         var n = this.nodes[index];
3965         for(var i = 0, len = records.length; i < len; i++){
3966             var d = this.prepareData(records[i].data, i, records[i]);
3967             if(n){
3968                 this.tpl.insertBefore(n, d);
3969             }else{
3970                 
3971                 this.tpl.append(this.el, d);
3972             }
3973         }
3974         this.updateIndexes(index);
3975     },
3976
3977     onRemove : function(ds, record, index){
3978        // Roo.log('onRemove');
3979         this.clearSelections();
3980         var el = this.dataName  ?
3981             this.el.child('.roo-tpl-' + this.dataName) :
3982             this.el; 
3983         
3984         el.dom.removeChild(this.nodes[index]);
3985         this.updateIndexes(index);
3986     },
3987
3988     /**
3989      * Refresh an individual node.
3990      * @param {Number} index
3991      */
3992     refreshNode : function(index){
3993         this.onUpdate(this.store, this.store.getAt(index));
3994     },
3995
3996     updateIndexes : function(startIndex, endIndex){
3997         var ns = this.nodes;
3998         startIndex = startIndex || 0;
3999         endIndex = endIndex || ns.length - 1;
4000         for(var i = startIndex; i <= endIndex; i++){
4001             ns[i].nodeIndex = i;
4002         }
4003     },
4004
4005     /**
4006      * Changes the data store this view uses and refresh the view.
4007      * @param {Store} store
4008      */
4009     setStore : function(store, initial){
4010         if(!initial && this.store){
4011             this.store.un("datachanged", this.refresh);
4012             this.store.un("add", this.onAdd);
4013             this.store.un("remove", this.onRemove);
4014             this.store.un("update", this.onUpdate);
4015             this.store.un("clear", this.refresh);
4016             this.store.un("beforeload", this.onBeforeLoad);
4017             this.store.un("load", this.onLoad);
4018             this.store.un("loadexception", this.onLoad);
4019         }
4020         if(store){
4021           
4022             store.on("datachanged", this.refresh, this);
4023             store.on("add", this.onAdd, this);
4024             store.on("remove", this.onRemove, this);
4025             store.on("update", this.onUpdate, this);
4026             store.on("clear", this.refresh, this);
4027             store.on("beforeload", this.onBeforeLoad, this);
4028             store.on("load", this.onLoad, this);
4029             store.on("loadexception", this.onLoad, this);
4030         }
4031         
4032         if(store){
4033             this.refresh();
4034         }
4035     },
4036     /**
4037      * onbeforeLoad - masks the loading area.
4038      *
4039      */
4040     onBeforeLoad : function(store,opts)
4041     {
4042          //Roo.log('onBeforeLoad');   
4043         if (!opts.add) {
4044             this.el.update("");
4045         }
4046         this.el.mask(this.mask ? this.mask : "Loading" ); 
4047     },
4048     onLoad : function ()
4049     {
4050         this.el.unmask();
4051     },
4052     
4053
4054     /**
4055      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4056      * @param {HTMLElement} node
4057      * @return {HTMLElement} The template node
4058      */
4059     findItemFromChild : function(node){
4060         var el = this.dataName  ?
4061             this.el.child('.roo-tpl-' + this.dataName,true) :
4062             this.el.dom; 
4063         
4064         if(!node || node.parentNode == el){
4065                     return node;
4066             }
4067             var p = node.parentNode;
4068             while(p && p != el){
4069             if(p.parentNode == el){
4070                 return p;
4071             }
4072             p = p.parentNode;
4073         }
4074             return null;
4075     },
4076
4077     /** @ignore */
4078     onClick : function(e){
4079         var item = this.findItemFromChild(e.getTarget());
4080         if(item){
4081             var index = this.indexOf(item);
4082             if(this.onItemClick(item, index, e) !== false){
4083                 this.fireEvent("click", this, index, item, e);
4084             }
4085         }else{
4086             this.clearSelections();
4087         }
4088     },
4089
4090     /** @ignore */
4091     onContextMenu : function(e){
4092         var item = this.findItemFromChild(e.getTarget());
4093         if(item){
4094             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4095         }
4096     },
4097
4098     /** @ignore */
4099     onDblClick : function(e){
4100         var item = this.findItemFromChild(e.getTarget());
4101         if(item){
4102             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4103         }
4104     },
4105
4106     onItemClick : function(item, index, e)
4107     {
4108         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4109             return false;
4110         }
4111         if (this.toggleSelect) {
4112             var m = this.isSelected(item) ? 'unselect' : 'select';
4113             //Roo.log(m);
4114             var _t = this;
4115             _t[m](item, true, false);
4116             return true;
4117         }
4118         if(this.multiSelect || this.singleSelect){
4119             if(this.multiSelect && e.shiftKey && this.lastSelection){
4120                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4121             }else{
4122                 this.select(item, this.multiSelect && e.ctrlKey);
4123                 this.lastSelection = item;
4124             }
4125             
4126             if(!this.tickable){
4127                 e.preventDefault();
4128             }
4129             
4130         }
4131         return true;
4132     },
4133
4134     /**
4135      * Get the number of selected nodes.
4136      * @return {Number}
4137      */
4138     getSelectionCount : function(){
4139         return this.selections.length;
4140     },
4141
4142     /**
4143      * Get the currently selected nodes.
4144      * @return {Array} An array of HTMLElements
4145      */
4146     getSelectedNodes : function(){
4147         return this.selections;
4148     },
4149
4150     /**
4151      * Get the indexes of the selected nodes.
4152      * @return {Array}
4153      */
4154     getSelectedIndexes : function(){
4155         var indexes = [], s = this.selections;
4156         for(var i = 0, len = s.length; i < len; i++){
4157             indexes.push(s[i].nodeIndex);
4158         }
4159         return indexes;
4160     },
4161
4162     /**
4163      * Clear all selections
4164      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4165      */
4166     clearSelections : function(suppressEvent){
4167         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4168             this.cmp.elements = this.selections;
4169             this.cmp.removeClass(this.selectedClass);
4170             this.selections = [];
4171             if(!suppressEvent){
4172                 this.fireEvent("selectionchange", this, this.selections);
4173             }
4174         }
4175     },
4176
4177     /**
4178      * Returns true if the passed node is selected
4179      * @param {HTMLElement/Number} node The node or node index
4180      * @return {Boolean}
4181      */
4182     isSelected : function(node){
4183         var s = this.selections;
4184         if(s.length < 1){
4185             return false;
4186         }
4187         node = this.getNode(node);
4188         return s.indexOf(node) !== -1;
4189     },
4190
4191     /**
4192      * Selects nodes.
4193      * @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
4194      * @param {Boolean} keepExisting (optional) true to keep existing selections
4195      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4196      */
4197     select : function(nodeInfo, keepExisting, suppressEvent){
4198         if(nodeInfo instanceof Array){
4199             if(!keepExisting){
4200                 this.clearSelections(true);
4201             }
4202             for(var i = 0, len = nodeInfo.length; i < len; i++){
4203                 this.select(nodeInfo[i], true, true);
4204             }
4205             return;
4206         } 
4207         var node = this.getNode(nodeInfo);
4208         if(!node || this.isSelected(node)){
4209             return; // already selected.
4210         }
4211         if(!keepExisting){
4212             this.clearSelections(true);
4213         }
4214         
4215         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4216             Roo.fly(node).addClass(this.selectedClass);
4217             this.selections.push(node);
4218             if(!suppressEvent){
4219                 this.fireEvent("selectionchange", this, this.selections);
4220             }
4221         }
4222         
4223         
4224     },
4225       /**
4226      * Unselects nodes.
4227      * @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
4228      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4229      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4230      */
4231     unselect : function(nodeInfo, keepExisting, suppressEvent)
4232     {
4233         if(nodeInfo instanceof Array){
4234             Roo.each(this.selections, function(s) {
4235                 this.unselect(s, nodeInfo);
4236             }, this);
4237             return;
4238         }
4239         var node = this.getNode(nodeInfo);
4240         if(!node || !this.isSelected(node)){
4241             //Roo.log("not selected");
4242             return; // not selected.
4243         }
4244         // fireevent???
4245         var ns = [];
4246         Roo.each(this.selections, function(s) {
4247             if (s == node ) {
4248                 Roo.fly(node).removeClass(this.selectedClass);
4249
4250                 return;
4251             }
4252             ns.push(s);
4253         },this);
4254         
4255         this.selections= ns;
4256         this.fireEvent("selectionchange", this, this.selections);
4257     },
4258
4259     /**
4260      * Gets a template node.
4261      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4262      * @return {HTMLElement} The node or null if it wasn't found
4263      */
4264     getNode : function(nodeInfo){
4265         if(typeof nodeInfo == "string"){
4266             return document.getElementById(nodeInfo);
4267         }else if(typeof nodeInfo == "number"){
4268             return this.nodes[nodeInfo];
4269         }
4270         return nodeInfo;
4271     },
4272
4273     /**
4274      * Gets a range template nodes.
4275      * @param {Number} startIndex
4276      * @param {Number} endIndex
4277      * @return {Array} An array of nodes
4278      */
4279     getNodes : function(start, end){
4280         var ns = this.nodes;
4281         start = start || 0;
4282         end = typeof end == "undefined" ? ns.length - 1 : end;
4283         var nodes = [];
4284         if(start <= end){
4285             for(var i = start; i <= end; i++){
4286                 nodes.push(ns[i]);
4287             }
4288         } else{
4289             for(var i = start; i >= end; i--){
4290                 nodes.push(ns[i]);
4291             }
4292         }
4293         return nodes;
4294     },
4295
4296     /**
4297      * Finds the index of the passed node
4298      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4299      * @return {Number} The index of the node or -1
4300      */
4301     indexOf : function(node){
4302         node = this.getNode(node);
4303         if(typeof node.nodeIndex == "number"){
4304             return node.nodeIndex;
4305         }
4306         var ns = this.nodes;
4307         for(var i = 0, len = ns.length; i < len; i++){
4308             if(ns[i] == node){
4309                 return i;
4310             }
4311         }
4312         return -1;
4313     }
4314 });
4315 /*
4316  * Based on:
4317  * Ext JS Library 1.1.1
4318  * Copyright(c) 2006-2007, Ext JS, LLC.
4319  *
4320  * Originally Released Under LGPL - original licence link has changed is not relivant.
4321  *
4322  * Fork - LGPL
4323  * <script type="text/javascript">
4324  */
4325
4326 /**
4327  * @class Roo.JsonView
4328  * @extends Roo.View
4329  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4330 <pre><code>
4331 var view = new Roo.JsonView({
4332     container: "my-element",
4333     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4334     multiSelect: true, 
4335     jsonRoot: "data" 
4336 });
4337
4338 // listen for node click?
4339 view.on("click", function(vw, index, node, e){
4340     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4341 });
4342
4343 // direct load of JSON data
4344 view.load("foobar.php");
4345
4346 // Example from my blog list
4347 var tpl = new Roo.Template(
4348     '&lt;div class="entry"&gt;' +
4349     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4350     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4351     "&lt;/div&gt;&lt;hr /&gt;"
4352 );
4353
4354 var moreView = new Roo.JsonView({
4355     container :  "entry-list", 
4356     template : tpl,
4357     jsonRoot: "posts"
4358 });
4359 moreView.on("beforerender", this.sortEntries, this);
4360 moreView.load({
4361     url: "/blog/get-posts.php",
4362     params: "allposts=true",
4363     text: "Loading Blog Entries..."
4364 });
4365 </code></pre>
4366
4367 * Note: old code is supported with arguments : (container, template, config)
4368
4369
4370  * @constructor
4371  * Create a new JsonView
4372  * 
4373  * @param {Object} config The config object
4374  * 
4375  */
4376 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4377     
4378     
4379     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4380
4381     var um = this.el.getUpdateManager();
4382     um.setRenderer(this);
4383     um.on("update", this.onLoad, this);
4384     um.on("failure", this.onLoadException, this);
4385
4386     /**
4387      * @event beforerender
4388      * Fires before rendering of the downloaded JSON data.
4389      * @param {Roo.JsonView} this
4390      * @param {Object} data The JSON data loaded
4391      */
4392     /**
4393      * @event load
4394      * Fires when data is loaded.
4395      * @param {Roo.JsonView} this
4396      * @param {Object} data The JSON data loaded
4397      * @param {Object} response The raw Connect response object
4398      */
4399     /**
4400      * @event loadexception
4401      * Fires when loading fails.
4402      * @param {Roo.JsonView} this
4403      * @param {Object} response The raw Connect response object
4404      */
4405     this.addEvents({
4406         'beforerender' : true,
4407         'load' : true,
4408         'loadexception' : true
4409     });
4410 };
4411 Roo.extend(Roo.JsonView, Roo.View, {
4412     /**
4413      * @type {String} The root property in the loaded JSON object that contains the data
4414      */
4415     jsonRoot : "",
4416
4417     /**
4418      * Refreshes the view.
4419      */
4420     refresh : function(){
4421         this.clearSelections();
4422         this.el.update("");
4423         var html = [];
4424         var o = this.jsonData;
4425         if(o && o.length > 0){
4426             for(var i = 0, len = o.length; i < len; i++){
4427                 var data = this.prepareData(o[i], i, o);
4428                 html[html.length] = this.tpl.apply(data);
4429             }
4430         }else{
4431             html.push(this.emptyText);
4432         }
4433         this.el.update(html.join(""));
4434         this.nodes = this.el.dom.childNodes;
4435         this.updateIndexes(0);
4436     },
4437
4438     /**
4439      * 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.
4440      * @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:
4441      <pre><code>
4442      view.load({
4443          url: "your-url.php",
4444          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4445          callback: yourFunction,
4446          scope: yourObject, //(optional scope)
4447          discardUrl: false,
4448          nocache: false,
4449          text: "Loading...",
4450          timeout: 30,
4451          scripts: false
4452      });
4453      </code></pre>
4454      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4455      * 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.
4456      * @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}
4457      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4458      * @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.
4459      */
4460     load : function(){
4461         var um = this.el.getUpdateManager();
4462         um.update.apply(um, arguments);
4463     },
4464
4465     // note - render is a standard framework call...
4466     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4467     render : function(el, response){
4468         
4469         this.clearSelections();
4470         this.el.update("");
4471         var o;
4472         try{
4473             if (response != '') {
4474                 o = Roo.util.JSON.decode(response.responseText);
4475                 if(this.jsonRoot){
4476                     
4477                     o = o[this.jsonRoot];
4478                 }
4479             }
4480         } catch(e){
4481         }
4482         /**
4483          * The current JSON data or null
4484          */
4485         this.jsonData = o;
4486         this.beforeRender();
4487         this.refresh();
4488     },
4489
4490 /**
4491  * Get the number of records in the current JSON dataset
4492  * @return {Number}
4493  */
4494     getCount : function(){
4495         return this.jsonData ? this.jsonData.length : 0;
4496     },
4497
4498 /**
4499  * Returns the JSON object for the specified node(s)
4500  * @param {HTMLElement/Array} node The node or an array of nodes
4501  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4502  * you get the JSON object for the node
4503  */
4504     getNodeData : function(node){
4505         if(node instanceof Array){
4506             var data = [];
4507             for(var i = 0, len = node.length; i < len; i++){
4508                 data.push(this.getNodeData(node[i]));
4509             }
4510             return data;
4511         }
4512         return this.jsonData[this.indexOf(node)] || null;
4513     },
4514
4515     beforeRender : function(){
4516         this.snapshot = this.jsonData;
4517         if(this.sortInfo){
4518             this.sort.apply(this, this.sortInfo);
4519         }
4520         this.fireEvent("beforerender", this, this.jsonData);
4521     },
4522
4523     onLoad : function(el, o){
4524         this.fireEvent("load", this, this.jsonData, o);
4525     },
4526
4527     onLoadException : function(el, o){
4528         this.fireEvent("loadexception", this, o);
4529     },
4530
4531 /**
4532  * Filter the data by a specific property.
4533  * @param {String} property A property on your JSON objects
4534  * @param {String/RegExp} value Either string that the property values
4535  * should start with, or a RegExp to test against the property
4536  */
4537     filter : function(property, value){
4538         if(this.jsonData){
4539             var data = [];
4540             var ss = this.snapshot;
4541             if(typeof value == "string"){
4542                 var vlen = value.length;
4543                 if(vlen == 0){
4544                     this.clearFilter();
4545                     return;
4546                 }
4547                 value = value.toLowerCase();
4548                 for(var i = 0, len = ss.length; i < len; i++){
4549                     var o = ss[i];
4550                     if(o[property].substr(0, vlen).toLowerCase() == value){
4551                         data.push(o);
4552                     }
4553                 }
4554             } else if(value.exec){ // regex?
4555                 for(var i = 0, len = ss.length; i < len; i++){
4556                     var o = ss[i];
4557                     if(value.test(o[property])){
4558                         data.push(o);
4559                     }
4560                 }
4561             } else{
4562                 return;
4563             }
4564             this.jsonData = data;
4565             this.refresh();
4566         }
4567     },
4568
4569 /**
4570  * Filter by a function. The passed function will be called with each
4571  * object in the current dataset. If the function returns true the value is kept,
4572  * otherwise it is filtered.
4573  * @param {Function} fn
4574  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4575  */
4576     filterBy : function(fn, scope){
4577         if(this.jsonData){
4578             var data = [];
4579             var ss = this.snapshot;
4580             for(var i = 0, len = ss.length; i < len; i++){
4581                 var o = ss[i];
4582                 if(fn.call(scope || this, o)){
4583                     data.push(o);
4584                 }
4585             }
4586             this.jsonData = data;
4587             this.refresh();
4588         }
4589     },
4590
4591 /**
4592  * Clears the current filter.
4593  */
4594     clearFilter : function(){
4595         if(this.snapshot && this.jsonData != this.snapshot){
4596             this.jsonData = this.snapshot;
4597             this.refresh();
4598         }
4599     },
4600
4601
4602 /**
4603  * Sorts the data for this view and refreshes it.
4604  * @param {String} property A property on your JSON objects to sort on
4605  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
4606  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
4607  */
4608     sort : function(property, dir, sortType){
4609         this.sortInfo = Array.prototype.slice.call(arguments, 0);
4610         if(this.jsonData){
4611             var p = property;
4612             var dsc = dir && dir.toLowerCase() == "desc";
4613             var f = function(o1, o2){
4614                 var v1 = sortType ? sortType(o1[p]) : o1[p];
4615                 var v2 = sortType ? sortType(o2[p]) : o2[p];
4616                 ;
4617                 if(v1 < v2){
4618                     return dsc ? +1 : -1;
4619                 } else if(v1 > v2){
4620                     return dsc ? -1 : +1;
4621                 } else{
4622                     return 0;
4623                 }
4624             };
4625             this.jsonData.sort(f);
4626             this.refresh();
4627             if(this.jsonData != this.snapshot){
4628                 this.snapshot.sort(f);
4629             }
4630         }
4631     }
4632 });/*
4633  * Based on:
4634  * Ext JS Library 1.1.1
4635  * Copyright(c) 2006-2007, Ext JS, LLC.
4636  *
4637  * Originally Released Under LGPL - original licence link has changed is not relivant.
4638  *
4639  * Fork - LGPL
4640  * <script type="text/javascript">
4641  */
4642  
4643
4644 /**
4645  * @class Roo.ColorPalette
4646  * @extends Roo.Component
4647  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
4648  * Here's an example of typical usage:
4649  * <pre><code>
4650 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
4651 cp.render('my-div');
4652
4653 cp.on('select', function(palette, selColor){
4654     // do something with selColor
4655 });
4656 </code></pre>
4657  * @constructor
4658  * Create a new ColorPalette
4659  * @param {Object} config The config object
4660  */
4661 Roo.ColorPalette = function(config){
4662     Roo.ColorPalette.superclass.constructor.call(this, config);
4663     this.addEvents({
4664         /**
4665              * @event select
4666              * Fires when a color is selected
4667              * @param {ColorPalette} this
4668              * @param {String} color The 6-digit color hex code (without the # symbol)
4669              */
4670         select: true
4671     });
4672
4673     if(this.handler){
4674         this.on("select", this.handler, this.scope, true);
4675     }
4676 };
4677 Roo.extend(Roo.ColorPalette, Roo.Component, {
4678     /**
4679      * @cfg {String} itemCls
4680      * The CSS class to apply to the containing element (defaults to "x-color-palette")
4681      */
4682     itemCls : "x-color-palette",
4683     /**
4684      * @cfg {String} value
4685      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
4686      * the hex codes are case-sensitive.
4687      */
4688     value : null,
4689     clickEvent:'click',
4690     // private
4691     ctype: "Roo.ColorPalette",
4692
4693     /**
4694      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
4695      */
4696     allowReselect : false,
4697
4698     /**
4699      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
4700      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
4701      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
4702      * of colors with the width setting until the box is symmetrical.</p>
4703      * <p>You can override individual colors if needed:</p>
4704      * <pre><code>
4705 var cp = new Roo.ColorPalette();
4706 cp.colors[0] = "FF0000";  // change the first box to red
4707 </code></pre>
4708
4709 Or you can provide a custom array of your own for complete control:
4710 <pre><code>
4711 var cp = new Roo.ColorPalette();
4712 cp.colors = ["000000", "993300", "333300"];
4713 </code></pre>
4714      * @type Array
4715      */
4716     colors : [
4717         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
4718         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
4719         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
4720         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
4721         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
4722     ],
4723
4724     // private
4725     onRender : function(container, position){
4726         var t = new Roo.MasterTemplate(
4727             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
4728         );
4729         var c = this.colors;
4730         for(var i = 0, len = c.length; i < len; i++){
4731             t.add([c[i]]);
4732         }
4733         var el = document.createElement("div");
4734         el.className = this.itemCls;
4735         t.overwrite(el);
4736         container.dom.insertBefore(el, position);
4737         this.el = Roo.get(el);
4738         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
4739         if(this.clickEvent != 'click'){
4740             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
4741         }
4742     },
4743
4744     // private
4745     afterRender : function(){
4746         Roo.ColorPalette.superclass.afterRender.call(this);
4747         if(this.value){
4748             var s = this.value;
4749             this.value = null;
4750             this.select(s);
4751         }
4752     },
4753
4754     // private
4755     handleClick : function(e, t){
4756         e.preventDefault();
4757         if(!this.disabled){
4758             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
4759             this.select(c.toUpperCase());
4760         }
4761     },
4762
4763     /**
4764      * Selects the specified color in the palette (fires the select event)
4765      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
4766      */
4767     select : function(color){
4768         color = color.replace("#", "");
4769         if(color != this.value || this.allowReselect){
4770             var el = this.el;
4771             if(this.value){
4772                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
4773             }
4774             el.child("a.color-"+color).addClass("x-color-palette-sel");
4775             this.value = color;
4776             this.fireEvent("select", this, color);
4777         }
4778     }
4779 });/*
4780  * Based on:
4781  * Ext JS Library 1.1.1
4782  * Copyright(c) 2006-2007, Ext JS, LLC.
4783  *
4784  * Originally Released Under LGPL - original licence link has changed is not relivant.
4785  *
4786  * Fork - LGPL
4787  * <script type="text/javascript">
4788  */
4789  
4790 /**
4791  * @class Roo.DatePicker
4792  * @extends Roo.Component
4793  * Simple date picker class.
4794  * @constructor
4795  * Create a new DatePicker
4796  * @param {Object} config The config object
4797  */
4798 Roo.DatePicker = function(config){
4799     Roo.DatePicker.superclass.constructor.call(this, config);
4800
4801     this.value = config && config.value ?
4802                  config.value.clearTime() : new Date().clearTime();
4803
4804     this.addEvents({
4805         /**
4806              * @event select
4807              * Fires when a date is selected
4808              * @param {DatePicker} this
4809              * @param {Date} date The selected date
4810              */
4811         'select': true,
4812         /**
4813              * @event monthchange
4814              * Fires when the displayed month changes 
4815              * @param {DatePicker} this
4816              * @param {Date} date The selected month
4817              */
4818         'monthchange': true
4819     });
4820
4821     if(this.handler){
4822         this.on("select", this.handler,  this.scope || this);
4823     }
4824     // build the disabledDatesRE
4825     if(!this.disabledDatesRE && this.disabledDates){
4826         var dd = this.disabledDates;
4827         var re = "(?:";
4828         for(var i = 0; i < dd.length; i++){
4829             re += dd[i];
4830             if(i != dd.length-1) {
4831                 re += "|";
4832             }
4833         }
4834         this.disabledDatesRE = new RegExp(re + ")");
4835     }
4836 };
4837
4838 Roo.extend(Roo.DatePicker, Roo.Component, {
4839     /**
4840      * @cfg {String} todayText
4841      * The text to display on the button that selects the current date (defaults to "Today")
4842      */
4843     todayText : "Today",
4844     /**
4845      * @cfg {String} okText
4846      * The text to display on the ok button
4847      */
4848     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
4849     /**
4850      * @cfg {String} cancelText
4851      * The text to display on the cancel button
4852      */
4853     cancelText : "Cancel",
4854     /**
4855      * @cfg {String} todayTip
4856      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
4857      */
4858     todayTip : "{0} (Spacebar)",
4859     /**
4860      * @cfg {Date} minDate
4861      * Minimum allowable date (JavaScript date object, defaults to null)
4862      */
4863     minDate : null,
4864     /**
4865      * @cfg {Date} maxDate
4866      * Maximum allowable date (JavaScript date object, defaults to null)
4867      */
4868     maxDate : null,
4869     /**
4870      * @cfg {String} minText
4871      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
4872      */
4873     minText : "This date is before the minimum date",
4874     /**
4875      * @cfg {String} maxText
4876      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
4877      */
4878     maxText : "This date is after the maximum date",
4879     /**
4880      * @cfg {String} format
4881      * The default date format string which can be overriden for localization support.  The format must be
4882      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
4883      */
4884     format : "m/d/y",
4885     /**
4886      * @cfg {Array} disabledDays
4887      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
4888      */
4889     disabledDays : null,
4890     /**
4891      * @cfg {String} disabledDaysText
4892      * The tooltip to display when the date falls on a disabled day (defaults to "")
4893      */
4894     disabledDaysText : "",
4895     /**
4896      * @cfg {RegExp} disabledDatesRE
4897      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
4898      */
4899     disabledDatesRE : null,
4900     /**
4901      * @cfg {String} disabledDatesText
4902      * The tooltip text to display when the date falls on a disabled date (defaults to "")
4903      */
4904     disabledDatesText : "",
4905     /**
4906      * @cfg {Boolean} constrainToViewport
4907      * True to constrain the date picker to the viewport (defaults to true)
4908      */
4909     constrainToViewport : true,
4910     /**
4911      * @cfg {Array} monthNames
4912      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
4913      */
4914     monthNames : Date.monthNames,
4915     /**
4916      * @cfg {Array} dayNames
4917      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
4918      */
4919     dayNames : Date.dayNames,
4920     /**
4921      * @cfg {String} nextText
4922      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
4923      */
4924     nextText: 'Next Month (Control+Right)',
4925     /**
4926      * @cfg {String} prevText
4927      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
4928      */
4929     prevText: 'Previous Month (Control+Left)',
4930     /**
4931      * @cfg {String} monthYearText
4932      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
4933      */
4934     monthYearText: 'Choose a month (Control+Up/Down to move years)',
4935     /**
4936      * @cfg {Number} startDay
4937      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
4938      */
4939     startDay : 0,
4940     /**
4941      * @cfg {Bool} showClear
4942      * Show a clear button (usefull for date form elements that can be blank.)
4943      */
4944     
4945     showClear: false,
4946     
4947     /**
4948      * Sets the value of the date field
4949      * @param {Date} value The date to set
4950      */
4951     setValue : function(value){
4952         var old = this.value;
4953         
4954         if (typeof(value) == 'string') {
4955          
4956             value = Date.parseDate(value, this.format);
4957         }
4958         if (!value) {
4959             value = new Date();
4960         }
4961         
4962         this.value = value.clearTime(true);
4963         if(this.el){
4964             this.update(this.value);
4965         }
4966     },
4967
4968     /**
4969      * Gets the current selected value of the date field
4970      * @return {Date} The selected date
4971      */
4972     getValue : function(){
4973         return this.value;
4974     },
4975
4976     // private
4977     focus : function(){
4978         if(this.el){
4979             this.update(this.activeDate);
4980         }
4981     },
4982
4983     // privateval
4984     onRender : function(container, position){
4985         
4986         var m = [
4987              '<table cellspacing="0">',
4988                 '<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>',
4989                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
4990         var dn = this.dayNames;
4991         for(var i = 0; i < 7; i++){
4992             var d = this.startDay+i;
4993             if(d > 6){
4994                 d = d-7;
4995             }
4996             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
4997         }
4998         m[m.length] = "</tr></thead><tbody><tr>";
4999         for(var i = 0; i < 42; i++) {
5000             if(i % 7 == 0 && i != 0){
5001                 m[m.length] = "</tr><tr>";
5002             }
5003             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5004         }
5005         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5006             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5007
5008         var el = document.createElement("div");
5009         el.className = "x-date-picker";
5010         el.innerHTML = m.join("");
5011
5012         container.dom.insertBefore(el, position);
5013
5014         this.el = Roo.get(el);
5015         this.eventEl = Roo.get(el.firstChild);
5016
5017         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5018             handler: this.showPrevMonth,
5019             scope: this,
5020             preventDefault:true,
5021             stopDefault:true
5022         });
5023
5024         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5025             handler: this.showNextMonth,
5026             scope: this,
5027             preventDefault:true,
5028             stopDefault:true
5029         });
5030
5031         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5032
5033         this.monthPicker = this.el.down('div.x-date-mp');
5034         this.monthPicker.enableDisplayMode('block');
5035         
5036         var kn = new Roo.KeyNav(this.eventEl, {
5037             "left" : function(e){
5038                 e.ctrlKey ?
5039                     this.showPrevMonth() :
5040                     this.update(this.activeDate.add("d", -1));
5041             },
5042
5043             "right" : function(e){
5044                 e.ctrlKey ?
5045                     this.showNextMonth() :
5046                     this.update(this.activeDate.add("d", 1));
5047             },
5048
5049             "up" : function(e){
5050                 e.ctrlKey ?
5051                     this.showNextYear() :
5052                     this.update(this.activeDate.add("d", -7));
5053             },
5054
5055             "down" : function(e){
5056                 e.ctrlKey ?
5057                     this.showPrevYear() :
5058                     this.update(this.activeDate.add("d", 7));
5059             },
5060
5061             "pageUp" : function(e){
5062                 this.showNextMonth();
5063             },
5064
5065             "pageDown" : function(e){
5066                 this.showPrevMonth();
5067             },
5068
5069             "enter" : function(e){
5070                 e.stopPropagation();
5071                 return true;
5072             },
5073
5074             scope : this
5075         });
5076
5077         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5078
5079         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5080
5081         this.el.unselectable();
5082         
5083         this.cells = this.el.select("table.x-date-inner tbody td");
5084         this.textNodes = this.el.query("table.x-date-inner tbody span");
5085
5086         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5087             text: "&#160;",
5088             tooltip: this.monthYearText
5089         });
5090
5091         this.mbtn.on('click', this.showMonthPicker, this);
5092         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5093
5094
5095         var today = (new Date()).dateFormat(this.format);
5096         
5097         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5098         if (this.showClear) {
5099             baseTb.add( new Roo.Toolbar.Fill());
5100         }
5101         baseTb.add({
5102             text: String.format(this.todayText, today),
5103             tooltip: String.format(this.todayTip, today),
5104             handler: this.selectToday,
5105             scope: this
5106         });
5107         
5108         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5109             
5110         //});
5111         if (this.showClear) {
5112             
5113             baseTb.add( new Roo.Toolbar.Fill());
5114             baseTb.add({
5115                 text: '&#160;',
5116                 cls: 'x-btn-icon x-btn-clear',
5117                 handler: function() {
5118                     //this.value = '';
5119                     this.fireEvent("select", this, '');
5120                 },
5121                 scope: this
5122             });
5123         }
5124         
5125         
5126         if(Roo.isIE){
5127             this.el.repaint();
5128         }
5129         this.update(this.value);
5130     },
5131
5132     createMonthPicker : function(){
5133         if(!this.monthPicker.dom.firstChild){
5134             var buf = ['<table border="0" cellspacing="0">'];
5135             for(var i = 0; i < 6; i++){
5136                 buf.push(
5137                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5138                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5139                     i == 0 ?
5140                     '<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>' :
5141                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5142                 );
5143             }
5144             buf.push(
5145                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5146                     this.okText,
5147                     '</button><button type="button" class="x-date-mp-cancel">',
5148                     this.cancelText,
5149                     '</button></td></tr>',
5150                 '</table>'
5151             );
5152             this.monthPicker.update(buf.join(''));
5153             this.monthPicker.on('click', this.onMonthClick, this);
5154             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5155
5156             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5157             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5158
5159             this.mpMonths.each(function(m, a, i){
5160                 i += 1;
5161                 if((i%2) == 0){
5162                     m.dom.xmonth = 5 + Math.round(i * .5);
5163                 }else{
5164                     m.dom.xmonth = Math.round((i-1) * .5);
5165                 }
5166             });
5167         }
5168     },
5169
5170     showMonthPicker : function(){
5171         this.createMonthPicker();
5172         var size = this.el.getSize();
5173         this.monthPicker.setSize(size);
5174         this.monthPicker.child('table').setSize(size);
5175
5176         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5177         this.updateMPMonth(this.mpSelMonth);
5178         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5179         this.updateMPYear(this.mpSelYear);
5180
5181         this.monthPicker.slideIn('t', {duration:.2});
5182     },
5183
5184     updateMPYear : function(y){
5185         this.mpyear = y;
5186         var ys = this.mpYears.elements;
5187         for(var i = 1; i <= 10; i++){
5188             var td = ys[i-1], y2;
5189             if((i%2) == 0){
5190                 y2 = y + Math.round(i * .5);
5191                 td.firstChild.innerHTML = y2;
5192                 td.xyear = y2;
5193             }else{
5194                 y2 = y - (5-Math.round(i * .5));
5195                 td.firstChild.innerHTML = y2;
5196                 td.xyear = y2;
5197             }
5198             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5199         }
5200     },
5201
5202     updateMPMonth : function(sm){
5203         this.mpMonths.each(function(m, a, i){
5204             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5205         });
5206     },
5207
5208     selectMPMonth: function(m){
5209         
5210     },
5211
5212     onMonthClick : function(e, t){
5213         e.stopEvent();
5214         var el = new Roo.Element(t), pn;
5215         if(el.is('button.x-date-mp-cancel')){
5216             this.hideMonthPicker();
5217         }
5218         else if(el.is('button.x-date-mp-ok')){
5219             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5220             this.hideMonthPicker();
5221         }
5222         else if(pn = el.up('td.x-date-mp-month', 2)){
5223             this.mpMonths.removeClass('x-date-mp-sel');
5224             pn.addClass('x-date-mp-sel');
5225             this.mpSelMonth = pn.dom.xmonth;
5226         }
5227         else if(pn = el.up('td.x-date-mp-year', 2)){
5228             this.mpYears.removeClass('x-date-mp-sel');
5229             pn.addClass('x-date-mp-sel');
5230             this.mpSelYear = pn.dom.xyear;
5231         }
5232         else if(el.is('a.x-date-mp-prev')){
5233             this.updateMPYear(this.mpyear-10);
5234         }
5235         else if(el.is('a.x-date-mp-next')){
5236             this.updateMPYear(this.mpyear+10);
5237         }
5238     },
5239
5240     onMonthDblClick : function(e, t){
5241         e.stopEvent();
5242         var el = new Roo.Element(t), pn;
5243         if(pn = el.up('td.x-date-mp-month', 2)){
5244             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5245             this.hideMonthPicker();
5246         }
5247         else if(pn = el.up('td.x-date-mp-year', 2)){
5248             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5249             this.hideMonthPicker();
5250         }
5251     },
5252
5253     hideMonthPicker : function(disableAnim){
5254         if(this.monthPicker){
5255             if(disableAnim === true){
5256                 this.monthPicker.hide();
5257             }else{
5258                 this.monthPicker.slideOut('t', {duration:.2});
5259             }
5260         }
5261     },
5262
5263     // private
5264     showPrevMonth : function(e){
5265         this.update(this.activeDate.add("mo", -1));
5266     },
5267
5268     // private
5269     showNextMonth : function(e){
5270         this.update(this.activeDate.add("mo", 1));
5271     },
5272
5273     // private
5274     showPrevYear : function(){
5275         this.update(this.activeDate.add("y", -1));
5276     },
5277
5278     // private
5279     showNextYear : function(){
5280         this.update(this.activeDate.add("y", 1));
5281     },
5282
5283     // private
5284     handleMouseWheel : function(e){
5285         var delta = e.getWheelDelta();
5286         if(delta > 0){
5287             this.showPrevMonth();
5288             e.stopEvent();
5289         } else if(delta < 0){
5290             this.showNextMonth();
5291             e.stopEvent();
5292         }
5293     },
5294
5295     // private
5296     handleDateClick : function(e, t){
5297         e.stopEvent();
5298         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5299             this.setValue(new Date(t.dateValue));
5300             this.fireEvent("select", this, this.value);
5301         }
5302     },
5303
5304     // private
5305     selectToday : function(){
5306         this.setValue(new Date().clearTime());
5307         this.fireEvent("select", this, this.value);
5308     },
5309
5310     // private
5311     update : function(date)
5312     {
5313         var vd = this.activeDate;
5314         this.activeDate = date;
5315         if(vd && this.el){
5316             var t = date.getTime();
5317             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5318                 this.cells.removeClass("x-date-selected");
5319                 this.cells.each(function(c){
5320                    if(c.dom.firstChild.dateValue == t){
5321                        c.addClass("x-date-selected");
5322                        setTimeout(function(){
5323                             try{c.dom.firstChild.focus();}catch(e){}
5324                        }, 50);
5325                        return false;
5326                    }
5327                 });
5328                 return;
5329             }
5330         }
5331         
5332         var days = date.getDaysInMonth();
5333         var firstOfMonth = date.getFirstDateOfMonth();
5334         var startingPos = firstOfMonth.getDay()-this.startDay;
5335
5336         if(startingPos <= this.startDay){
5337             startingPos += 7;
5338         }
5339
5340         var pm = date.add("mo", -1);
5341         var prevStart = pm.getDaysInMonth()-startingPos;
5342
5343         var cells = this.cells.elements;
5344         var textEls = this.textNodes;
5345         days += startingPos;
5346
5347         // convert everything to numbers so it's fast
5348         var day = 86400000;
5349         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5350         var today = new Date().clearTime().getTime();
5351         var sel = date.clearTime().getTime();
5352         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5353         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5354         var ddMatch = this.disabledDatesRE;
5355         var ddText = this.disabledDatesText;
5356         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5357         var ddaysText = this.disabledDaysText;
5358         var format = this.format;
5359
5360         var setCellClass = function(cal, cell){
5361             cell.title = "";
5362             var t = d.getTime();
5363             cell.firstChild.dateValue = t;
5364             if(t == today){
5365                 cell.className += " x-date-today";
5366                 cell.title = cal.todayText;
5367             }
5368             if(t == sel){
5369                 cell.className += " x-date-selected";
5370                 setTimeout(function(){
5371                     try{cell.firstChild.focus();}catch(e){}
5372                 }, 50);
5373             }
5374             // disabling
5375             if(t < min) {
5376                 cell.className = " x-date-disabled";
5377                 cell.title = cal.minText;
5378                 return;
5379             }
5380             if(t > max) {
5381                 cell.className = " x-date-disabled";
5382                 cell.title = cal.maxText;
5383                 return;
5384             }
5385             if(ddays){
5386                 if(ddays.indexOf(d.getDay()) != -1){
5387                     cell.title = ddaysText;
5388                     cell.className = " x-date-disabled";
5389                 }
5390             }
5391             if(ddMatch && format){
5392                 var fvalue = d.dateFormat(format);
5393                 if(ddMatch.test(fvalue)){
5394                     cell.title = ddText.replace("%0", fvalue);
5395                     cell.className = " x-date-disabled";
5396                 }
5397             }
5398         };
5399
5400         var i = 0;
5401         for(; i < startingPos; i++) {
5402             textEls[i].innerHTML = (++prevStart);
5403             d.setDate(d.getDate()+1);
5404             cells[i].className = "x-date-prevday";
5405             setCellClass(this, cells[i]);
5406         }
5407         for(; i < days; i++){
5408             intDay = i - startingPos + 1;
5409             textEls[i].innerHTML = (intDay);
5410             d.setDate(d.getDate()+1);
5411             cells[i].className = "x-date-active";
5412             setCellClass(this, cells[i]);
5413         }
5414         var extraDays = 0;
5415         for(; i < 42; i++) {
5416              textEls[i].innerHTML = (++extraDays);
5417              d.setDate(d.getDate()+1);
5418              cells[i].className = "x-date-nextday";
5419              setCellClass(this, cells[i]);
5420         }
5421
5422         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5423         this.fireEvent('monthchange', this, date);
5424         
5425         if(!this.internalRender){
5426             var main = this.el.dom.firstChild;
5427             var w = main.offsetWidth;
5428             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5429             Roo.fly(main).setWidth(w);
5430             this.internalRender = true;
5431             // opera does not respect the auto grow header center column
5432             // then, after it gets a width opera refuses to recalculate
5433             // without a second pass
5434             if(Roo.isOpera && !this.secondPass){
5435                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5436                 this.secondPass = true;
5437                 this.update.defer(10, this, [date]);
5438             }
5439         }
5440         
5441         
5442     }
5443 });        /*
5444  * Based on:
5445  * Ext JS Library 1.1.1
5446  * Copyright(c) 2006-2007, Ext JS, LLC.
5447  *
5448  * Originally Released Under LGPL - original licence link has changed is not relivant.
5449  *
5450  * Fork - LGPL
5451  * <script type="text/javascript">
5452  */
5453 /**
5454  * @class Roo.TabPanel
5455  * @extends Roo.util.Observable
5456  * A lightweight tab container.
5457  * <br><br>
5458  * Usage:
5459  * <pre><code>
5460 // basic tabs 1, built from existing content
5461 var tabs = new Roo.TabPanel("tabs1");
5462 tabs.addTab("script", "View Script");
5463 tabs.addTab("markup", "View Markup");
5464 tabs.activate("script");
5465
5466 // more advanced tabs, built from javascript
5467 var jtabs = new Roo.TabPanel("jtabs");
5468 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5469
5470 // set up the UpdateManager
5471 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5472 var updater = tab2.getUpdateManager();
5473 updater.setDefaultUrl("ajax1.htm");
5474 tab2.on('activate', updater.refresh, updater, true);
5475
5476 // Use setUrl for Ajax loading
5477 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5478 tab3.setUrl("ajax2.htm", null, true);
5479
5480 // Disabled tab
5481 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5482 tab4.disable();
5483
5484 jtabs.activate("jtabs-1");
5485  * </code></pre>
5486  * @constructor
5487  * Create a new TabPanel.
5488  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5489  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5490  */
5491 Roo.TabPanel = function(container, config){
5492     /**
5493     * The container element for this TabPanel.
5494     * @type Roo.Element
5495     */
5496     this.el = Roo.get(container, true);
5497     if(config){
5498         if(typeof config == "boolean"){
5499             this.tabPosition = config ? "bottom" : "top";
5500         }else{
5501             Roo.apply(this, config);
5502         }
5503     }
5504     if(this.tabPosition == "bottom"){
5505         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5506         this.el.addClass("x-tabs-bottom");
5507     }
5508     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5509     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5510     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5511     if(Roo.isIE){
5512         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5513     }
5514     if(this.tabPosition != "bottom"){
5515         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5516          * @type Roo.Element
5517          */
5518         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5519         this.el.addClass("x-tabs-top");
5520     }
5521     this.items = [];
5522
5523     this.bodyEl.setStyle("position", "relative");
5524
5525     this.active = null;
5526     this.activateDelegate = this.activate.createDelegate(this);
5527
5528     this.addEvents({
5529         /**
5530          * @event tabchange
5531          * Fires when the active tab changes
5532          * @param {Roo.TabPanel} this
5533          * @param {Roo.TabPanelItem} activePanel The new active tab
5534          */
5535         "tabchange": true,
5536         /**
5537          * @event beforetabchange
5538          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5539          * @param {Roo.TabPanel} this
5540          * @param {Object} e Set cancel to true on this object to cancel the tab change
5541          * @param {Roo.TabPanelItem} tab The tab being changed to
5542          */
5543         "beforetabchange" : true
5544     });
5545
5546     Roo.EventManager.onWindowResize(this.onResize, this);
5547     this.cpad = this.el.getPadding("lr");
5548     this.hiddenCount = 0;
5549
5550
5551     // toolbar on the tabbar support...
5552     if (this.toolbar) {
5553         var tcfg = this.toolbar;
5554         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
5555         this.toolbar = new Roo.Toolbar(tcfg);
5556         if (Roo.isSafari) {
5557             var tbl = tcfg.container.child('table', true);
5558             tbl.setAttribute('width', '100%');
5559         }
5560         
5561     }
5562    
5563
5564
5565     Roo.TabPanel.superclass.constructor.call(this);
5566 };
5567
5568 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5569     /*
5570      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5571      */
5572     tabPosition : "top",
5573     /*
5574      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5575      */
5576     currentTabWidth : 0,
5577     /*
5578      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5579      */
5580     minTabWidth : 40,
5581     /*
5582      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5583      */
5584     maxTabWidth : 250,
5585     /*
5586      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5587      */
5588     preferredTabWidth : 175,
5589     /*
5590      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
5591      */
5592     resizeTabs : false,
5593     /*
5594      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
5595      */
5596     monitorResize : true,
5597     /*
5598      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
5599      */
5600     toolbar : false,
5601
5602     /**
5603      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
5604      * @param {String} id The id of the div to use <b>or create</b>
5605      * @param {String} text The text for the tab
5606      * @param {String} content (optional) Content to put in the TabPanelItem body
5607      * @param {Boolean} closable (optional) True to create a close icon on the tab
5608      * @return {Roo.TabPanelItem} The created TabPanelItem
5609      */
5610     addTab : function(id, text, content, closable){
5611         var item = new Roo.TabPanelItem(this, id, text, closable);
5612         this.addTabItem(item);
5613         if(content){
5614             item.setContent(content);
5615         }
5616         return item;
5617     },
5618
5619     /**
5620      * Returns the {@link Roo.TabPanelItem} with the specified id/index
5621      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
5622      * @return {Roo.TabPanelItem}
5623      */
5624     getTab : function(id){
5625         return this.items[id];
5626     },
5627
5628     /**
5629      * Hides the {@link Roo.TabPanelItem} with the specified id/index
5630      * @param {String/Number} id The id or index of the TabPanelItem to hide.
5631      */
5632     hideTab : function(id){
5633         var t = this.items[id];
5634         if(!t.isHidden()){
5635            t.setHidden(true);
5636            this.hiddenCount++;
5637            this.autoSizeTabs();
5638         }
5639     },
5640
5641     /**
5642      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
5643      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
5644      */
5645     unhideTab : function(id){
5646         var t = this.items[id];
5647         if(t.isHidden()){
5648            t.setHidden(false);
5649            this.hiddenCount--;
5650            this.autoSizeTabs();
5651         }
5652     },
5653
5654     /**
5655      * Adds an existing {@link Roo.TabPanelItem}.
5656      * @param {Roo.TabPanelItem} item The TabPanelItem to add
5657      */
5658     addTabItem : function(item){
5659         this.items[item.id] = item;
5660         this.items.push(item);
5661         if(this.resizeTabs){
5662            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
5663            this.autoSizeTabs();
5664         }else{
5665             item.autoSize();
5666         }
5667     },
5668
5669     /**
5670      * Removes a {@link Roo.TabPanelItem}.
5671      * @param {String/Number} id The id or index of the TabPanelItem to remove.
5672      */
5673     removeTab : function(id){
5674         var items = this.items;
5675         var tab = items[id];
5676         if(!tab) { return; }
5677         var index = items.indexOf(tab);
5678         if(this.active == tab && items.length > 1){
5679             var newTab = this.getNextAvailable(index);
5680             if(newTab) {
5681                 newTab.activate();
5682             }
5683         }
5684         this.stripEl.dom.removeChild(tab.pnode.dom);
5685         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
5686             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
5687         }
5688         items.splice(index, 1);
5689         delete this.items[tab.id];
5690         tab.fireEvent("close", tab);
5691         tab.purgeListeners();
5692         this.autoSizeTabs();
5693     },
5694
5695     getNextAvailable : function(start){
5696         var items = this.items;
5697         var index = start;
5698         // look for a next tab that will slide over to
5699         // replace the one being removed
5700         while(index < items.length){
5701             var item = items[++index];
5702             if(item && !item.isHidden()){
5703                 return item;
5704             }
5705         }
5706         // if one isn't found select the previous tab (on the left)
5707         index = start;
5708         while(index >= 0){
5709             var item = items[--index];
5710             if(item && !item.isHidden()){
5711                 return item;
5712             }
5713         }
5714         return null;
5715     },
5716
5717     /**
5718      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
5719      * @param {String/Number} id The id or index of the TabPanelItem to disable.
5720      */
5721     disableTab : function(id){
5722         var tab = this.items[id];
5723         if(tab && this.active != tab){
5724             tab.disable();
5725         }
5726     },
5727
5728     /**
5729      * Enables a {@link Roo.TabPanelItem} that is disabled.
5730      * @param {String/Number} id The id or index of the TabPanelItem to enable.
5731      */
5732     enableTab : function(id){
5733         var tab = this.items[id];
5734         tab.enable();
5735     },
5736
5737     /**
5738      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
5739      * @param {String/Number} id The id or index of the TabPanelItem to activate.
5740      * @return {Roo.TabPanelItem} The TabPanelItem.
5741      */
5742     activate : function(id){
5743         var tab = this.items[id];
5744         if(!tab){
5745             return null;
5746         }
5747         if(tab == this.active || tab.disabled){
5748             return tab;
5749         }
5750         var e = {};
5751         this.fireEvent("beforetabchange", this, e, tab);
5752         if(e.cancel !== true && !tab.disabled){
5753             if(this.active){
5754                 this.active.hide();
5755             }
5756             this.active = this.items[id];
5757             this.active.show();
5758             this.fireEvent("tabchange", this, this.active);
5759         }
5760         return tab;
5761     },
5762
5763     /**
5764      * Gets the active {@link Roo.TabPanelItem}.
5765      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
5766      */
5767     getActiveTab : function(){
5768         return this.active;
5769     },
5770
5771     /**
5772      * Updates the tab body element to fit the height of the container element
5773      * for overflow scrolling
5774      * @param {Number} targetHeight (optional) Override the starting height from the elements height
5775      */
5776     syncHeight : function(targetHeight){
5777         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
5778         var bm = this.bodyEl.getMargins();
5779         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
5780         this.bodyEl.setHeight(newHeight);
5781         return newHeight;
5782     },
5783
5784     onResize : function(){
5785         if(this.monitorResize){
5786             this.autoSizeTabs();
5787         }
5788     },
5789
5790     /**
5791      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
5792      */
5793     beginUpdate : function(){
5794         this.updating = true;
5795     },
5796
5797     /**
5798      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
5799      */
5800     endUpdate : function(){
5801         this.updating = false;
5802         this.autoSizeTabs();
5803     },
5804
5805     /**
5806      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
5807      */
5808     autoSizeTabs : function(){
5809         var count = this.items.length;
5810         var vcount = count - this.hiddenCount;
5811         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
5812             return;
5813         }
5814         var w = Math.max(this.el.getWidth() - this.cpad, 10);
5815         var availWidth = Math.floor(w / vcount);
5816         var b = this.stripBody;
5817         if(b.getWidth() > w){
5818             var tabs = this.items;
5819             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
5820             if(availWidth < this.minTabWidth){
5821                 /*if(!this.sleft){    // incomplete scrolling code
5822                     this.createScrollButtons();
5823                 }
5824                 this.showScroll();
5825                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
5826             }
5827         }else{
5828             if(this.currentTabWidth < this.preferredTabWidth){
5829                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
5830             }
5831         }
5832     },
5833
5834     /**
5835      * Returns the number of tabs in this TabPanel.
5836      * @return {Number}
5837      */
5838      getCount : function(){
5839          return this.items.length;
5840      },
5841
5842     /**
5843      * Resizes all the tabs to the passed width
5844      * @param {Number} The new width
5845      */
5846     setTabWidth : function(width){
5847         this.currentTabWidth = width;
5848         for(var i = 0, len = this.items.length; i < len; i++) {
5849                 if(!this.items[i].isHidden()) {
5850                 this.items[i].setWidth(width);
5851             }
5852         }
5853     },
5854
5855     /**
5856      * Destroys this TabPanel
5857      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
5858      */
5859     destroy : function(removeEl){
5860         Roo.EventManager.removeResizeListener(this.onResize, this);
5861         for(var i = 0, len = this.items.length; i < len; i++){
5862             this.items[i].purgeListeners();
5863         }
5864         if(removeEl === true){
5865             this.el.update("");
5866             this.el.remove();
5867         }
5868     }
5869 });
5870
5871 /**
5872  * @class Roo.TabPanelItem
5873  * @extends Roo.util.Observable
5874  * Represents an individual item (tab plus body) in a TabPanel.
5875  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
5876  * @param {String} id The id of this TabPanelItem
5877  * @param {String} text The text for the tab of this TabPanelItem
5878  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
5879  */
5880 Roo.TabPanelItem = function(tabPanel, id, text, closable){
5881     /**
5882      * The {@link Roo.TabPanel} this TabPanelItem belongs to
5883      * @type Roo.TabPanel
5884      */
5885     this.tabPanel = tabPanel;
5886     /**
5887      * The id for this TabPanelItem
5888      * @type String
5889      */
5890     this.id = id;
5891     /** @private */
5892     this.disabled = false;
5893     /** @private */
5894     this.text = text;
5895     /** @private */
5896     this.loaded = false;
5897     this.closable = closable;
5898
5899     /**
5900      * The body element for this TabPanelItem.
5901      * @type Roo.Element
5902      */
5903     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
5904     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
5905     this.bodyEl.setStyle("display", "block");
5906     this.bodyEl.setStyle("zoom", "1");
5907     this.hideAction();
5908
5909     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
5910     /** @private */
5911     this.el = Roo.get(els.el, true);
5912     this.inner = Roo.get(els.inner, true);
5913     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
5914     this.pnode = Roo.get(els.el.parentNode, true);
5915     this.el.on("mousedown", this.onTabMouseDown, this);
5916     this.el.on("click", this.onTabClick, this);
5917     /** @private */
5918     if(closable){
5919         var c = Roo.get(els.close, true);
5920         c.dom.title = this.closeText;
5921         c.addClassOnOver("close-over");
5922         c.on("click", this.closeClick, this);
5923      }
5924
5925     this.addEvents({
5926          /**
5927          * @event activate
5928          * Fires when this tab becomes the active tab.
5929          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5930          * @param {Roo.TabPanelItem} this
5931          */
5932         "activate": true,
5933         /**
5934          * @event beforeclose
5935          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
5936          * @param {Roo.TabPanelItem} this
5937          * @param {Object} e Set cancel to true on this object to cancel the close.
5938          */
5939         "beforeclose": true,
5940         /**
5941          * @event close
5942          * Fires when this tab is closed.
5943          * @param {Roo.TabPanelItem} this
5944          */
5945          "close": true,
5946         /**
5947          * @event deactivate
5948          * Fires when this tab is no longer the active tab.
5949          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5950          * @param {Roo.TabPanelItem} this
5951          */
5952          "deactivate" : true
5953     });
5954     this.hidden = false;
5955
5956     Roo.TabPanelItem.superclass.constructor.call(this);
5957 };
5958
5959 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
5960     purgeListeners : function(){
5961        Roo.util.Observable.prototype.purgeListeners.call(this);
5962        this.el.removeAllListeners();
5963     },
5964     /**
5965      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
5966      */
5967     show : function(){
5968         this.pnode.addClass("on");
5969         this.showAction();
5970         if(Roo.isOpera){
5971             this.tabPanel.stripWrap.repaint();
5972         }
5973         this.fireEvent("activate", this.tabPanel, this);
5974     },
5975
5976     /**
5977      * Returns true if this tab is the active tab.
5978      * @return {Boolean}
5979      */
5980     isActive : function(){
5981         return this.tabPanel.getActiveTab() == this;
5982     },
5983
5984     /**
5985      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
5986      */
5987     hide : function(){
5988         this.pnode.removeClass("on");
5989         this.hideAction();
5990         this.fireEvent("deactivate", this.tabPanel, this);
5991     },
5992
5993     hideAction : function(){
5994         this.bodyEl.hide();
5995         this.bodyEl.setStyle("position", "absolute");
5996         this.bodyEl.setLeft("-20000px");
5997         this.bodyEl.setTop("-20000px");
5998     },
5999
6000     showAction : function(){
6001         this.bodyEl.setStyle("position", "relative");
6002         this.bodyEl.setTop("");
6003         this.bodyEl.setLeft("");
6004         this.bodyEl.show();
6005     },
6006
6007     /**
6008      * Set the tooltip for the tab.
6009      * @param {String} tooltip The tab's tooltip
6010      */
6011     setTooltip : function(text){
6012         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6013             this.textEl.dom.qtip = text;
6014             this.textEl.dom.removeAttribute('title');
6015         }else{
6016             this.textEl.dom.title = text;
6017         }
6018     },
6019
6020     onTabClick : function(e){
6021         e.preventDefault();
6022         this.tabPanel.activate(this.id);
6023     },
6024
6025     onTabMouseDown : function(e){
6026         e.preventDefault();
6027         this.tabPanel.activate(this.id);
6028     },
6029
6030     getWidth : function(){
6031         return this.inner.getWidth();
6032     },
6033
6034     setWidth : function(width){
6035         var iwidth = width - this.pnode.getPadding("lr");
6036         this.inner.setWidth(iwidth);
6037         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6038         this.pnode.setWidth(width);
6039     },
6040
6041     /**
6042      * Show or hide the tab
6043      * @param {Boolean} hidden True to hide or false to show.
6044      */
6045     setHidden : function(hidden){
6046         this.hidden = hidden;
6047         this.pnode.setStyle("display", hidden ? "none" : "");
6048     },
6049
6050     /**
6051      * Returns true if this tab is "hidden"
6052      * @return {Boolean}
6053      */
6054     isHidden : function(){
6055         return this.hidden;
6056     },
6057
6058     /**
6059      * Returns the text for this tab
6060      * @return {String}
6061      */
6062     getText : function(){
6063         return this.text;
6064     },
6065
6066     autoSize : function(){
6067         //this.el.beginMeasure();
6068         this.textEl.setWidth(1);
6069         /*
6070          *  #2804 [new] Tabs in Roojs
6071          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6072          */
6073         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6074         //this.el.endMeasure();
6075     },
6076
6077     /**
6078      * Sets the text for the tab (Note: this also sets the tooltip text)
6079      * @param {String} text The tab's text and tooltip
6080      */
6081     setText : function(text){
6082         this.text = text;
6083         this.textEl.update(text);
6084         this.setTooltip(text);
6085         if(!this.tabPanel.resizeTabs){
6086             this.autoSize();
6087         }
6088     },
6089     /**
6090      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6091      */
6092     activate : function(){
6093         this.tabPanel.activate(this.id);
6094     },
6095
6096     /**
6097      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6098      */
6099     disable : function(){
6100         if(this.tabPanel.active != this){
6101             this.disabled = true;
6102             this.pnode.addClass("disabled");
6103         }
6104     },
6105
6106     /**
6107      * Enables this TabPanelItem if it was previously disabled.
6108      */
6109     enable : function(){
6110         this.disabled = false;
6111         this.pnode.removeClass("disabled");
6112     },
6113
6114     /**
6115      * Sets the content for this TabPanelItem.
6116      * @param {String} content The content
6117      * @param {Boolean} loadScripts true to look for and load scripts
6118      */
6119     setContent : function(content, loadScripts){
6120         this.bodyEl.update(content, loadScripts);
6121     },
6122
6123     /**
6124      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6125      * @return {Roo.UpdateManager} The UpdateManager
6126      */
6127     getUpdateManager : function(){
6128         return this.bodyEl.getUpdateManager();
6129     },
6130
6131     /**
6132      * Set a URL to be used to load the content for this TabPanelItem.
6133      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6134      * @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)
6135      * @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)
6136      * @return {Roo.UpdateManager} The UpdateManager
6137      */
6138     setUrl : function(url, params, loadOnce){
6139         if(this.refreshDelegate){
6140             this.un('activate', this.refreshDelegate);
6141         }
6142         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6143         this.on("activate", this.refreshDelegate);
6144         return this.bodyEl.getUpdateManager();
6145     },
6146
6147     /** @private */
6148     _handleRefresh : function(url, params, loadOnce){
6149         if(!loadOnce || !this.loaded){
6150             var updater = this.bodyEl.getUpdateManager();
6151             updater.update(url, params, this._setLoaded.createDelegate(this));
6152         }
6153     },
6154
6155     /**
6156      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6157      *   Will fail silently if the setUrl method has not been called.
6158      *   This does not activate the panel, just updates its content.
6159      */
6160     refresh : function(){
6161         if(this.refreshDelegate){
6162            this.loaded = false;
6163            this.refreshDelegate();
6164         }
6165     },
6166
6167     /** @private */
6168     _setLoaded : function(){
6169         this.loaded = true;
6170     },
6171
6172     /** @private */
6173     closeClick : function(e){
6174         var o = {};
6175         e.stopEvent();
6176         this.fireEvent("beforeclose", this, o);
6177         if(o.cancel !== true){
6178             this.tabPanel.removeTab(this.id);
6179         }
6180     },
6181     /**
6182      * The text displayed in the tooltip for the close icon.
6183      * @type String
6184      */
6185     closeText : "Close this tab"
6186 });
6187
6188 /** @private */
6189 Roo.TabPanel.prototype.createStrip = function(container){
6190     var strip = document.createElement("div");
6191     strip.className = "x-tabs-wrap";
6192     container.appendChild(strip);
6193     return strip;
6194 };
6195 /** @private */
6196 Roo.TabPanel.prototype.createStripList = function(strip){
6197     // div wrapper for retard IE
6198     // returns the "tr" element.
6199     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6200         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6201         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6202     return strip.firstChild.firstChild.firstChild.firstChild;
6203 };
6204 /** @private */
6205 Roo.TabPanel.prototype.createBody = function(container){
6206     var body = document.createElement("div");
6207     Roo.id(body, "tab-body");
6208     Roo.fly(body).addClass("x-tabs-body");
6209     container.appendChild(body);
6210     return body;
6211 };
6212 /** @private */
6213 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6214     var body = Roo.getDom(id);
6215     if(!body){
6216         body = document.createElement("div");
6217         body.id = id;
6218     }
6219     Roo.fly(body).addClass("x-tabs-item-body");
6220     bodyEl.insertBefore(body, bodyEl.firstChild);
6221     return body;
6222 };
6223 /** @private */
6224 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6225     var td = document.createElement("td");
6226     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6227     //stripEl.appendChild(td);
6228     if(closable){
6229         td.className = "x-tabs-closable";
6230         if(!this.closeTpl){
6231             this.closeTpl = new Roo.Template(
6232                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6233                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6234                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6235             );
6236         }
6237         var el = this.closeTpl.overwrite(td, {"text": text});
6238         var close = el.getElementsByTagName("div")[0];
6239         var inner = el.getElementsByTagName("em")[0];
6240         return {"el": el, "close": close, "inner": inner};
6241     } else {
6242         if(!this.tabTpl){
6243             this.tabTpl = new Roo.Template(
6244                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6245                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6246             );
6247         }
6248         var el = this.tabTpl.overwrite(td, {"text": text});
6249         var inner = el.getElementsByTagName("em")[0];
6250         return {"el": el, "inner": inner};
6251     }
6252 };/*
6253  * Based on:
6254  * Ext JS Library 1.1.1
6255  * Copyright(c) 2006-2007, Ext JS, LLC.
6256  *
6257  * Originally Released Under LGPL - original licence link has changed is not relivant.
6258  *
6259  * Fork - LGPL
6260  * <script type="text/javascript">
6261  */
6262
6263 /**
6264  * @class Roo.Button
6265  * @extends Roo.util.Observable
6266  * Simple Button class
6267  * @cfg {String} text The button text
6268  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6269  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6270  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6271  * @cfg {Object} scope The scope of the handler
6272  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6273  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6274  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6275  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6276  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6277  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6278    applies if enableToggle = true)
6279  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6280  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6281   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6282  * @constructor
6283  * Create a new button
6284  * @param {Object} config The config object
6285  */
6286 Roo.Button = function(renderTo, config)
6287 {
6288     if (!config) {
6289         config = renderTo;
6290         renderTo = config.renderTo || false;
6291     }
6292     
6293     Roo.apply(this, config);
6294     this.addEvents({
6295         /**
6296              * @event click
6297              * Fires when this button is clicked
6298              * @param {Button} this
6299              * @param {EventObject} e The click event
6300              */
6301             "click" : true,
6302         /**
6303              * @event toggle
6304              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6305              * @param {Button} this
6306              * @param {Boolean} pressed
6307              */
6308             "toggle" : true,
6309         /**
6310              * @event mouseover
6311              * Fires when the mouse hovers over the button
6312              * @param {Button} this
6313              * @param {Event} e The event object
6314              */
6315         'mouseover' : true,
6316         /**
6317              * @event mouseout
6318              * Fires when the mouse exits the button
6319              * @param {Button} this
6320              * @param {Event} e The event object
6321              */
6322         'mouseout': true,
6323          /**
6324              * @event render
6325              * Fires when the button is rendered
6326              * @param {Button} this
6327              */
6328         'render': true
6329     });
6330     if(this.menu){
6331         this.menu = Roo.menu.MenuMgr.get(this.menu);
6332     }
6333     // register listeners first!!  - so render can be captured..
6334     Roo.util.Observable.call(this);
6335     if(renderTo){
6336         this.render(renderTo);
6337     }
6338     
6339   
6340 };
6341
6342 Roo.extend(Roo.Button, Roo.util.Observable, {
6343     /**
6344      * 
6345      */
6346     
6347     /**
6348      * Read-only. True if this button is hidden
6349      * @type Boolean
6350      */
6351     hidden : false,
6352     /**
6353      * Read-only. True if this button is disabled
6354      * @type Boolean
6355      */
6356     disabled : false,
6357     /**
6358      * Read-only. True if this button is pressed (only if enableToggle = true)
6359      * @type Boolean
6360      */
6361     pressed : false,
6362
6363     /**
6364      * @cfg {Number} tabIndex 
6365      * The DOM tabIndex for this button (defaults to undefined)
6366      */
6367     tabIndex : undefined,
6368
6369     /**
6370      * @cfg {Boolean} enableToggle
6371      * True to enable pressed/not pressed toggling (defaults to false)
6372      */
6373     enableToggle: false,
6374     /**
6375      * @cfg {Roo.menu.Menu} menu
6376      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6377      */
6378     menu : undefined,
6379     /**
6380      * @cfg {String} menuAlign
6381      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6382      */
6383     menuAlign : "tl-bl?",
6384
6385     /**
6386      * @cfg {String} iconCls
6387      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6388      */
6389     iconCls : undefined,
6390     /**
6391      * @cfg {String} type
6392      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6393      */
6394     type : 'button',
6395
6396     // private
6397     menuClassTarget: 'tr',
6398
6399     /**
6400      * @cfg {String} clickEvent
6401      * The type of event to map to the button's event handler (defaults to 'click')
6402      */
6403     clickEvent : 'click',
6404
6405     /**
6406      * @cfg {Boolean} handleMouseEvents
6407      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6408      */
6409     handleMouseEvents : true,
6410
6411     /**
6412      * @cfg {String} tooltipType
6413      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6414      */
6415     tooltipType : 'qtip',
6416
6417     /**
6418      * @cfg {String} cls
6419      * A CSS class to apply to the button's main element.
6420      */
6421     
6422     /**
6423      * @cfg {Roo.Template} template (Optional)
6424      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6425      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6426      * require code modifications if required elements (e.g. a button) aren't present.
6427      */
6428
6429     // private
6430     render : function(renderTo){
6431         var btn;
6432         if(this.hideParent){
6433             this.parentEl = Roo.get(renderTo);
6434         }
6435         if(!this.dhconfig){
6436             if(!this.template){
6437                 if(!Roo.Button.buttonTemplate){
6438                     // hideous table template
6439                     Roo.Button.buttonTemplate = new Roo.Template(
6440                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6441                         '<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>',
6442                         "</tr></tbody></table>");
6443                 }
6444                 this.template = Roo.Button.buttonTemplate;
6445             }
6446             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6447             var btnEl = btn.child("button:first");
6448             btnEl.on('focus', this.onFocus, this);
6449             btnEl.on('blur', this.onBlur, this);
6450             if(this.cls){
6451                 btn.addClass(this.cls);
6452             }
6453             if(this.icon){
6454                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6455             }
6456             if(this.iconCls){
6457                 btnEl.addClass(this.iconCls);
6458                 if(!this.cls){
6459                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6460                 }
6461             }
6462             if(this.tabIndex !== undefined){
6463                 btnEl.dom.tabIndex = this.tabIndex;
6464             }
6465             if(this.tooltip){
6466                 if(typeof this.tooltip == 'object'){
6467                     Roo.QuickTips.tips(Roo.apply({
6468                           target: btnEl.id
6469                     }, this.tooltip));
6470                 } else {
6471                     btnEl.dom[this.tooltipType] = this.tooltip;
6472                 }
6473             }
6474         }else{
6475             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6476         }
6477         this.el = btn;
6478         if(this.id){
6479             this.el.dom.id = this.el.id = this.id;
6480         }
6481         if(this.menu){
6482             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6483             this.menu.on("show", this.onMenuShow, this);
6484             this.menu.on("hide", this.onMenuHide, this);
6485         }
6486         btn.addClass("x-btn");
6487         if(Roo.isIE && !Roo.isIE7){
6488             this.autoWidth.defer(1, this);
6489         }else{
6490             this.autoWidth();
6491         }
6492         if(this.handleMouseEvents){
6493             btn.on("mouseover", this.onMouseOver, this);
6494             btn.on("mouseout", this.onMouseOut, this);
6495             btn.on("mousedown", this.onMouseDown, this);
6496         }
6497         btn.on(this.clickEvent, this.onClick, this);
6498         //btn.on("mouseup", this.onMouseUp, this);
6499         if(this.hidden){
6500             this.hide();
6501         }
6502         if(this.disabled){
6503             this.disable();
6504         }
6505         Roo.ButtonToggleMgr.register(this);
6506         if(this.pressed){
6507             this.el.addClass("x-btn-pressed");
6508         }
6509         if(this.repeat){
6510             var repeater = new Roo.util.ClickRepeater(btn,
6511                 typeof this.repeat == "object" ? this.repeat : {}
6512             );
6513             repeater.on("click", this.onClick,  this);
6514         }
6515         
6516         this.fireEvent('render', this);
6517         
6518     },
6519     /**
6520      * Returns the button's underlying element
6521      * @return {Roo.Element} The element
6522      */
6523     getEl : function(){
6524         return this.el;  
6525     },
6526     
6527     /**
6528      * Destroys this Button and removes any listeners.
6529      */
6530     destroy : function(){
6531         Roo.ButtonToggleMgr.unregister(this);
6532         this.el.removeAllListeners();
6533         this.purgeListeners();
6534         this.el.remove();
6535     },
6536
6537     // private
6538     autoWidth : function(){
6539         if(this.el){
6540             this.el.setWidth("auto");
6541             if(Roo.isIE7 && Roo.isStrict){
6542                 var ib = this.el.child('button');
6543                 if(ib && ib.getWidth() > 20){
6544                     ib.clip();
6545                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6546                 }
6547             }
6548             if(this.minWidth){
6549                 if(this.hidden){
6550                     this.el.beginMeasure();
6551                 }
6552                 if(this.el.getWidth() < this.minWidth){
6553                     this.el.setWidth(this.minWidth);
6554                 }
6555                 if(this.hidden){
6556                     this.el.endMeasure();
6557                 }
6558             }
6559         }
6560     },
6561
6562     /**
6563      * Assigns this button's click handler
6564      * @param {Function} handler The function to call when the button is clicked
6565      * @param {Object} scope (optional) Scope for the function passed in
6566      */
6567     setHandler : function(handler, scope){
6568         this.handler = handler;
6569         this.scope = scope;  
6570     },
6571     
6572     /**
6573      * Sets this button's text
6574      * @param {String} text The button text
6575      */
6576     setText : function(text){
6577         this.text = text;
6578         if(this.el){
6579             this.el.child("td.x-btn-center button.x-btn-text").update(text);
6580         }
6581         this.autoWidth();
6582     },
6583     
6584     /**
6585      * Gets the text for this button
6586      * @return {String} The button text
6587      */
6588     getText : function(){
6589         return this.text;  
6590     },
6591     
6592     /**
6593      * Show this button
6594      */
6595     show: function(){
6596         this.hidden = false;
6597         if(this.el){
6598             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
6599         }
6600     },
6601     
6602     /**
6603      * Hide this button
6604      */
6605     hide: function(){
6606         this.hidden = true;
6607         if(this.el){
6608             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
6609         }
6610     },
6611     
6612     /**
6613      * Convenience function for boolean show/hide
6614      * @param {Boolean} visible True to show, false to hide
6615      */
6616     setVisible: function(visible){
6617         if(visible) {
6618             this.show();
6619         }else{
6620             this.hide();
6621         }
6622     },
6623     
6624     /**
6625      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
6626      * @param {Boolean} state (optional) Force a particular state
6627      */
6628     toggle : function(state){
6629         state = state === undefined ? !this.pressed : state;
6630         if(state != this.pressed){
6631             if(state){
6632                 this.el.addClass("x-btn-pressed");
6633                 this.pressed = true;
6634                 this.fireEvent("toggle", this, true);
6635             }else{
6636                 this.el.removeClass("x-btn-pressed");
6637                 this.pressed = false;
6638                 this.fireEvent("toggle", this, false);
6639             }
6640             if(this.toggleHandler){
6641                 this.toggleHandler.call(this.scope || this, this, state);
6642             }
6643         }
6644     },
6645     
6646     /**
6647      * Focus the button
6648      */
6649     focus : function(){
6650         this.el.child('button:first').focus();
6651     },
6652     
6653     /**
6654      * Disable this button
6655      */
6656     disable : function(){
6657         if(this.el){
6658             this.el.addClass("x-btn-disabled");
6659         }
6660         this.disabled = true;
6661     },
6662     
6663     /**
6664      * Enable this button
6665      */
6666     enable : function(){
6667         if(this.el){
6668             this.el.removeClass("x-btn-disabled");
6669         }
6670         this.disabled = false;
6671     },
6672
6673     /**
6674      * Convenience function for boolean enable/disable
6675      * @param {Boolean} enabled True to enable, false to disable
6676      */
6677     setDisabled : function(v){
6678         this[v !== true ? "enable" : "disable"]();
6679     },
6680
6681     // private
6682     onClick : function(e)
6683     {
6684         if(e){
6685             e.preventDefault();
6686         }
6687         if(e.button != 0){
6688             return;
6689         }
6690         if(!this.disabled){
6691             if(this.enableToggle){
6692                 this.toggle();
6693             }
6694             if(this.menu && !this.menu.isVisible()){
6695                 this.menu.show(this.el, this.menuAlign);
6696             }
6697             this.fireEvent("click", this, e);
6698             if(this.handler){
6699                 this.el.removeClass("x-btn-over");
6700                 this.handler.call(this.scope || this, this, e);
6701             }
6702         }
6703     },
6704     // private
6705     onMouseOver : function(e){
6706         if(!this.disabled){
6707             this.el.addClass("x-btn-over");
6708             this.fireEvent('mouseover', this, e);
6709         }
6710     },
6711     // private
6712     onMouseOut : function(e){
6713         if(!e.within(this.el,  true)){
6714             this.el.removeClass("x-btn-over");
6715             this.fireEvent('mouseout', this, e);
6716         }
6717     },
6718     // private
6719     onFocus : function(e){
6720         if(!this.disabled){
6721             this.el.addClass("x-btn-focus");
6722         }
6723     },
6724     // private
6725     onBlur : function(e){
6726         this.el.removeClass("x-btn-focus");
6727     },
6728     // private
6729     onMouseDown : function(e){
6730         if(!this.disabled && e.button == 0){
6731             this.el.addClass("x-btn-click");
6732             Roo.get(document).on('mouseup', this.onMouseUp, this);
6733         }
6734     },
6735     // private
6736     onMouseUp : function(e){
6737         if(e.button == 0){
6738             this.el.removeClass("x-btn-click");
6739             Roo.get(document).un('mouseup', this.onMouseUp, this);
6740         }
6741     },
6742     // private
6743     onMenuShow : function(e){
6744         this.el.addClass("x-btn-menu-active");
6745     },
6746     // private
6747     onMenuHide : function(e){
6748         this.el.removeClass("x-btn-menu-active");
6749     }   
6750 });
6751
6752 // Private utility class used by Button
6753 Roo.ButtonToggleMgr = function(){
6754    var groups = {};
6755    
6756    function toggleGroup(btn, state){
6757        if(state){
6758            var g = groups[btn.toggleGroup];
6759            for(var i = 0, l = g.length; i < l; i++){
6760                if(g[i] != btn){
6761                    g[i].toggle(false);
6762                }
6763            }
6764        }
6765    }
6766    
6767    return {
6768        register : function(btn){
6769            if(!btn.toggleGroup){
6770                return;
6771            }
6772            var g = groups[btn.toggleGroup];
6773            if(!g){
6774                g = groups[btn.toggleGroup] = [];
6775            }
6776            g.push(btn);
6777            btn.on("toggle", toggleGroup);
6778        },
6779        
6780        unregister : function(btn){
6781            if(!btn.toggleGroup){
6782                return;
6783            }
6784            var g = groups[btn.toggleGroup];
6785            if(g){
6786                g.remove(btn);
6787                btn.un("toggle", toggleGroup);
6788            }
6789        }
6790    };
6791 }();/*
6792  * Based on:
6793  * Ext JS Library 1.1.1
6794  * Copyright(c) 2006-2007, Ext JS, LLC.
6795  *
6796  * Originally Released Under LGPL - original licence link has changed is not relivant.
6797  *
6798  * Fork - LGPL
6799  * <script type="text/javascript">
6800  */
6801  
6802 /**
6803  * @class Roo.SplitButton
6804  * @extends Roo.Button
6805  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
6806  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
6807  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
6808  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
6809  * @cfg {String} arrowTooltip The title attribute of the arrow
6810  * @constructor
6811  * Create a new menu button
6812  * @param {String/HTMLElement/Element} renderTo The element to append the button to
6813  * @param {Object} config The config object
6814  */
6815 Roo.SplitButton = function(renderTo, config){
6816     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
6817     /**
6818      * @event arrowclick
6819      * Fires when this button's arrow is clicked
6820      * @param {SplitButton} this
6821      * @param {EventObject} e The click event
6822      */
6823     this.addEvents({"arrowclick":true});
6824 };
6825
6826 Roo.extend(Roo.SplitButton, Roo.Button, {
6827     render : function(renderTo){
6828         // this is one sweet looking template!
6829         var tpl = new Roo.Template(
6830             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
6831             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
6832             '<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>',
6833             "</tbody></table></td><td>",
6834             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
6835             '<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>',
6836             "</tbody></table></td></tr></table>"
6837         );
6838         var btn = tpl.append(renderTo, [this.text, this.type], true);
6839         var btnEl = btn.child("button");
6840         if(this.cls){
6841             btn.addClass(this.cls);
6842         }
6843         if(this.icon){
6844             btnEl.setStyle('background-image', 'url(' +this.icon +')');
6845         }
6846         if(this.iconCls){
6847             btnEl.addClass(this.iconCls);
6848             if(!this.cls){
6849                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6850             }
6851         }
6852         this.el = btn;
6853         if(this.handleMouseEvents){
6854             btn.on("mouseover", this.onMouseOver, this);
6855             btn.on("mouseout", this.onMouseOut, this);
6856             btn.on("mousedown", this.onMouseDown, this);
6857             btn.on("mouseup", this.onMouseUp, this);
6858         }
6859         btn.on(this.clickEvent, this.onClick, this);
6860         if(this.tooltip){
6861             if(typeof this.tooltip == 'object'){
6862                 Roo.QuickTips.tips(Roo.apply({
6863                       target: btnEl.id
6864                 }, this.tooltip));
6865             } else {
6866                 btnEl.dom[this.tooltipType] = this.tooltip;
6867             }
6868         }
6869         if(this.arrowTooltip){
6870             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
6871         }
6872         if(this.hidden){
6873             this.hide();
6874         }
6875         if(this.disabled){
6876             this.disable();
6877         }
6878         if(this.pressed){
6879             this.el.addClass("x-btn-pressed");
6880         }
6881         if(Roo.isIE && !Roo.isIE7){
6882             this.autoWidth.defer(1, this);
6883         }else{
6884             this.autoWidth();
6885         }
6886         if(this.menu){
6887             this.menu.on("show", this.onMenuShow, this);
6888             this.menu.on("hide", this.onMenuHide, this);
6889         }
6890         this.fireEvent('render', this);
6891     },
6892
6893     // private
6894     autoWidth : function(){
6895         if(this.el){
6896             var tbl = this.el.child("table:first");
6897             var tbl2 = this.el.child("table:last");
6898             this.el.setWidth("auto");
6899             tbl.setWidth("auto");
6900             if(Roo.isIE7 && Roo.isStrict){
6901                 var ib = this.el.child('button:first');
6902                 if(ib && ib.getWidth() > 20){
6903                     ib.clip();
6904                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6905                 }
6906             }
6907             if(this.minWidth){
6908                 if(this.hidden){
6909                     this.el.beginMeasure();
6910                 }
6911                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
6912                     tbl.setWidth(this.minWidth-tbl2.getWidth());
6913                 }
6914                 if(this.hidden){
6915                     this.el.endMeasure();
6916                 }
6917             }
6918             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
6919         } 
6920     },
6921     /**
6922      * Sets this button's click handler
6923      * @param {Function} handler The function to call when the button is clicked
6924      * @param {Object} scope (optional) Scope for the function passed above
6925      */
6926     setHandler : function(handler, scope){
6927         this.handler = handler;
6928         this.scope = scope;  
6929     },
6930     
6931     /**
6932      * Sets this button's arrow click handler
6933      * @param {Function} handler The function to call when the arrow is clicked
6934      * @param {Object} scope (optional) Scope for the function passed above
6935      */
6936     setArrowHandler : function(handler, scope){
6937         this.arrowHandler = handler;
6938         this.scope = scope;  
6939     },
6940     
6941     /**
6942      * Focus the button
6943      */
6944     focus : function(){
6945         if(this.el){
6946             this.el.child("button:first").focus();
6947         }
6948     },
6949
6950     // private
6951     onClick : function(e){
6952         e.preventDefault();
6953         if(!this.disabled){
6954             if(e.getTarget(".x-btn-menu-arrow-wrap")){
6955                 if(this.menu && !this.menu.isVisible()){
6956                     this.menu.show(this.el, this.menuAlign);
6957                 }
6958                 this.fireEvent("arrowclick", this, e);
6959                 if(this.arrowHandler){
6960                     this.arrowHandler.call(this.scope || this, this, e);
6961                 }
6962             }else{
6963                 this.fireEvent("click", this, e);
6964                 if(this.handler){
6965                     this.handler.call(this.scope || this, this, e);
6966                 }
6967             }
6968         }
6969     },
6970     // private
6971     onMouseDown : function(e){
6972         if(!this.disabled){
6973             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
6974         }
6975     },
6976     // private
6977     onMouseUp : function(e){
6978         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
6979     }   
6980 });
6981
6982
6983 // backwards compat
6984 Roo.MenuButton = Roo.SplitButton;/*
6985  * Based on:
6986  * Ext JS Library 1.1.1
6987  * Copyright(c) 2006-2007, Ext JS, LLC.
6988  *
6989  * Originally Released Under LGPL - original licence link has changed is not relivant.
6990  *
6991  * Fork - LGPL
6992  * <script type="text/javascript">
6993  */
6994
6995 /**
6996  * @class Roo.Toolbar
6997  * @children   Roo.Toolbar.Item Roo.form.Field
6998  * Basic Toolbar class.
6999  * @constructor
7000  * Creates a new Toolbar
7001  * @param {Object} container The config object
7002  */ 
7003 Roo.Toolbar = function(container, buttons, config)
7004 {
7005     /// old consturctor format still supported..
7006     if(container instanceof Array){ // omit the container for later rendering
7007         buttons = container;
7008         config = buttons;
7009         container = null;
7010     }
7011     if (typeof(container) == 'object' && container.xtype) {
7012         config = container;
7013         container = config.container;
7014         buttons = config.buttons || []; // not really - use items!!
7015     }
7016     var xitems = [];
7017     if (config && config.items) {
7018         xitems = config.items;
7019         delete config.items;
7020     }
7021     Roo.apply(this, config);
7022     this.buttons = buttons;
7023     
7024     if(container){
7025         this.render(container);
7026     }
7027     this.xitems = xitems;
7028     Roo.each(xitems, function(b) {
7029         this.add(b);
7030     }, this);
7031     
7032 };
7033
7034 Roo.Toolbar.prototype = {
7035     /**
7036      * @cfg {Array} items
7037      * array of button configs or elements to add (will be converted to a MixedCollection)
7038      */
7039     items: false,
7040     /**
7041      * @cfg {String/HTMLElement/Element} container
7042      * The id or element that will contain the toolbar
7043      */
7044     // private
7045     render : function(ct){
7046         this.el = Roo.get(ct);
7047         if(this.cls){
7048             this.el.addClass(this.cls);
7049         }
7050         // using a table allows for vertical alignment
7051         // 100% width is needed by Safari...
7052         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7053         this.tr = this.el.child("tr", true);
7054         var autoId = 0;
7055         this.items = new Roo.util.MixedCollection(false, function(o){
7056             return o.id || ("item" + (++autoId));
7057         });
7058         if(this.buttons){
7059             this.add.apply(this, this.buttons);
7060             delete this.buttons;
7061         }
7062     },
7063
7064     /**
7065      * Adds element(s) to the toolbar -- this function takes a variable number of 
7066      * arguments of mixed type and adds them to the toolbar.
7067      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7068      * <ul>
7069      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7070      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7071      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7072      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7073      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7074      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7075      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7076      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7077      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7078      * </ul>
7079      * @param {Mixed} arg2
7080      * @param {Mixed} etc.
7081      */
7082     add : function(){
7083         var a = arguments, l = a.length;
7084         for(var i = 0; i < l; i++){
7085             this._add(a[i]);
7086         }
7087     },
7088     // private..
7089     _add : function(el) {
7090         
7091         if (el.xtype) {
7092             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7093         }
7094         
7095         if (el.applyTo){ // some kind of form field
7096             return this.addField(el);
7097         } 
7098         if (el.render){ // some kind of Toolbar.Item
7099             return this.addItem(el);
7100         }
7101         if (typeof el == "string"){ // string
7102             if(el == "separator" || el == "-"){
7103                 return this.addSeparator();
7104             }
7105             if (el == " "){
7106                 return this.addSpacer();
7107             }
7108             if(el == "->"){
7109                 return this.addFill();
7110             }
7111             return this.addText(el);
7112             
7113         }
7114         if(el.tagName){ // element
7115             return this.addElement(el);
7116         }
7117         if(typeof el == "object"){ // must be button config?
7118             return this.addButton(el);
7119         }
7120         // and now what?!?!
7121         return false;
7122         
7123     },
7124     
7125     /**
7126      * Add an Xtype element
7127      * @param {Object} xtype Xtype Object
7128      * @return {Object} created Object
7129      */
7130     addxtype : function(e){
7131         return this.add(e);  
7132     },
7133     
7134     /**
7135      * Returns the Element for this toolbar.
7136      * @return {Roo.Element}
7137      */
7138     getEl : function(){
7139         return this.el;  
7140     },
7141     
7142     /**
7143      * Adds a separator
7144      * @return {Roo.Toolbar.Item} The separator item
7145      */
7146     addSeparator : function(){
7147         return this.addItem(new Roo.Toolbar.Separator());
7148     },
7149
7150     /**
7151      * Adds a spacer element
7152      * @return {Roo.Toolbar.Spacer} The spacer item
7153      */
7154     addSpacer : function(){
7155         return this.addItem(new Roo.Toolbar.Spacer());
7156     },
7157
7158     /**
7159      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7160      * @return {Roo.Toolbar.Fill} The fill item
7161      */
7162     addFill : function(){
7163         return this.addItem(new Roo.Toolbar.Fill());
7164     },
7165
7166     /**
7167      * Adds any standard HTML element to the toolbar
7168      * @param {String/HTMLElement/Element} el The element or id of the element to add
7169      * @return {Roo.Toolbar.Item} The element's item
7170      */
7171     addElement : function(el){
7172         return this.addItem(new Roo.Toolbar.Item(el));
7173     },
7174     /**
7175      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7176      * @type Roo.util.MixedCollection  
7177      */
7178     items : false,
7179      
7180     /**
7181      * Adds any Toolbar.Item or subclass
7182      * @param {Roo.Toolbar.Item} item
7183      * @return {Roo.Toolbar.Item} The item
7184      */
7185     addItem : function(item){
7186         var td = this.nextBlock();
7187         item.render(td);
7188         this.items.add(item);
7189         return item;
7190     },
7191     
7192     /**
7193      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7194      * @param {Object/Array} config A button config or array of configs
7195      * @return {Roo.Toolbar.Button/Array}
7196      */
7197     addButton : function(config){
7198         if(config instanceof Array){
7199             var buttons = [];
7200             for(var i = 0, len = config.length; i < len; i++) {
7201                 buttons.push(this.addButton(config[i]));
7202             }
7203             return buttons;
7204         }
7205         var b = config;
7206         if(!(config instanceof Roo.Toolbar.Button)){
7207             b = config.split ?
7208                 new Roo.Toolbar.SplitButton(config) :
7209                 new Roo.Toolbar.Button(config);
7210         }
7211         var td = this.nextBlock();
7212         b.render(td);
7213         this.items.add(b);
7214         return b;
7215     },
7216     
7217     /**
7218      * Adds text to the toolbar
7219      * @param {String} text The text to add
7220      * @return {Roo.Toolbar.Item} The element's item
7221      */
7222     addText : function(text){
7223         return this.addItem(new Roo.Toolbar.TextItem(text));
7224     },
7225     
7226     /**
7227      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7228      * @param {Number} index The index where the item is to be inserted
7229      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7230      * @return {Roo.Toolbar.Button/Item}
7231      */
7232     insertButton : function(index, item){
7233         if(item instanceof Array){
7234             var buttons = [];
7235             for(var i = 0, len = item.length; i < len; i++) {
7236                buttons.push(this.insertButton(index + i, item[i]));
7237             }
7238             return buttons;
7239         }
7240         if (!(item instanceof Roo.Toolbar.Button)){
7241            item = new Roo.Toolbar.Button(item);
7242         }
7243         var td = document.createElement("td");
7244         this.tr.insertBefore(td, this.tr.childNodes[index]);
7245         item.render(td);
7246         this.items.insert(index, item);
7247         return item;
7248     },
7249     
7250     /**
7251      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7252      * @param {Object} config
7253      * @return {Roo.Toolbar.Item} The element's item
7254      */
7255     addDom : function(config, returnEl){
7256         var td = this.nextBlock();
7257         Roo.DomHelper.overwrite(td, config);
7258         var ti = new Roo.Toolbar.Item(td.firstChild);
7259         ti.render(td);
7260         this.items.add(ti);
7261         return ti;
7262     },
7263
7264     /**
7265      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7266      * @type Roo.util.MixedCollection  
7267      */
7268     fields : false,
7269     
7270     /**
7271      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7272      * Note: the field should not have been rendered yet. For a field that has already been
7273      * rendered, use {@link #addElement}.
7274      * @param {Roo.form.Field} field
7275      * @return {Roo.ToolbarItem}
7276      */
7277      
7278       
7279     addField : function(field) {
7280         if (!this.fields) {
7281             var autoId = 0;
7282             this.fields = new Roo.util.MixedCollection(false, function(o){
7283                 return o.id || ("item" + (++autoId));
7284             });
7285
7286         }
7287         
7288         var td = this.nextBlock();
7289         field.render(td);
7290         var ti = new Roo.Toolbar.Item(td.firstChild);
7291         ti.render(td);
7292         this.items.add(ti);
7293         this.fields.add(field);
7294         return ti;
7295     },
7296     /**
7297      * Hide the toolbar
7298      * @method hide
7299      */
7300      
7301       
7302     hide : function()
7303     {
7304         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7305         this.el.child('div').hide();
7306     },
7307     /**
7308      * Show the toolbar
7309      * @method show
7310      */
7311     show : function()
7312     {
7313         this.el.child('div').show();
7314     },
7315       
7316     // private
7317     nextBlock : function(){
7318         var td = document.createElement("td");
7319         this.tr.appendChild(td);
7320         return td;
7321     },
7322
7323     // private
7324     destroy : function(){
7325         if(this.items){ // rendered?
7326             Roo.destroy.apply(Roo, this.items.items);
7327         }
7328         if(this.fields){ // rendered?
7329             Roo.destroy.apply(Roo, this.fields.items);
7330         }
7331         Roo.Element.uncache(this.el, this.tr);
7332     }
7333 };
7334
7335 /**
7336  * @class Roo.Toolbar.Item
7337  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7338  * @constructor
7339  * Creates a new Item
7340  * @param {HTMLElement} el 
7341  */
7342 Roo.Toolbar.Item = function(el){
7343     var cfg = {};
7344     if (typeof (el.xtype) != 'undefined') {
7345         cfg = el;
7346         el = cfg.el;
7347     }
7348     
7349     this.el = Roo.getDom(el);
7350     this.id = Roo.id(this.el);
7351     this.hidden = false;
7352     
7353     this.addEvents({
7354          /**
7355              * @event render
7356              * Fires when the button is rendered
7357              * @param {Button} this
7358              */
7359         'render': true
7360     });
7361     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7362 };
7363 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7364 //Roo.Toolbar.Item.prototype = {
7365     
7366     /**
7367      * Get this item's HTML Element
7368      * @return {HTMLElement}
7369      */
7370     getEl : function(){
7371        return this.el;  
7372     },
7373
7374     // private
7375     render : function(td){
7376         
7377          this.td = td;
7378         td.appendChild(this.el);
7379         
7380         this.fireEvent('render', this);
7381     },
7382     
7383     /**
7384      * Removes and destroys this item.
7385      */
7386     destroy : function(){
7387         this.td.parentNode.removeChild(this.td);
7388     },
7389     
7390     /**
7391      * Shows this item.
7392      */
7393     show: function(){
7394         this.hidden = false;
7395         this.td.style.display = "";
7396     },
7397     
7398     /**
7399      * Hides this item.
7400      */
7401     hide: function(){
7402         this.hidden = true;
7403         this.td.style.display = "none";
7404     },
7405     
7406     /**
7407      * Convenience function for boolean show/hide.
7408      * @param {Boolean} visible true to show/false to hide
7409      */
7410     setVisible: function(visible){
7411         if(visible) {
7412             this.show();
7413         }else{
7414             this.hide();
7415         }
7416     },
7417     
7418     /**
7419      * Try to focus this item.
7420      */
7421     focus : function(){
7422         Roo.fly(this.el).focus();
7423     },
7424     
7425     /**
7426      * Disables this item.
7427      */
7428     disable : function(){
7429         Roo.fly(this.td).addClass("x-item-disabled");
7430         this.disabled = true;
7431         this.el.disabled = true;
7432     },
7433     
7434     /**
7435      * Enables this item.
7436      */
7437     enable : function(){
7438         Roo.fly(this.td).removeClass("x-item-disabled");
7439         this.disabled = false;
7440         this.el.disabled = false;
7441     }
7442 });
7443
7444
7445 /**
7446  * @class Roo.Toolbar.Separator
7447  * @extends Roo.Toolbar.Item
7448  * A simple toolbar separator class
7449  * @constructor
7450  * Creates a new Separator
7451  */
7452 Roo.Toolbar.Separator = function(cfg){
7453     
7454     var s = document.createElement("span");
7455     s.className = "ytb-sep";
7456     if (cfg) {
7457         cfg.el = s;
7458     }
7459     
7460     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7461 };
7462 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7463     enable:Roo.emptyFn,
7464     disable:Roo.emptyFn,
7465     focus:Roo.emptyFn
7466 });
7467
7468 /**
7469  * @class Roo.Toolbar.Spacer
7470  * @extends Roo.Toolbar.Item
7471  * A simple element that adds extra horizontal space to a toolbar.
7472  * @constructor
7473  * Creates a new Spacer
7474  */
7475 Roo.Toolbar.Spacer = function(cfg){
7476     var s = document.createElement("div");
7477     s.className = "ytb-spacer";
7478     if (cfg) {
7479         cfg.el = s;
7480     }
7481     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7482 };
7483 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7484     enable:Roo.emptyFn,
7485     disable:Roo.emptyFn,
7486     focus:Roo.emptyFn
7487 });
7488
7489 /**
7490  * @class Roo.Toolbar.Fill
7491  * @extends Roo.Toolbar.Spacer
7492  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7493  * @constructor
7494  * Creates a new Spacer
7495  */
7496 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7497     // private
7498     render : function(td){
7499         td.style.width = '100%';
7500         Roo.Toolbar.Fill.superclass.render.call(this, td);
7501     }
7502 });
7503
7504 /**
7505  * @class Roo.Toolbar.TextItem
7506  * @extends Roo.Toolbar.Item
7507  * A simple class that renders text directly into a toolbar.
7508  * @constructor
7509  * Creates a new TextItem
7510  * @cfg {string} text 
7511  */
7512 Roo.Toolbar.TextItem = function(cfg){
7513     var  text = cfg || "";
7514     if (typeof(cfg) == 'object') {
7515         text = cfg.text || "";
7516     }  else {
7517         cfg = null;
7518     }
7519     var s = document.createElement("span");
7520     s.className = "ytb-text";
7521     s.innerHTML = text;
7522     if (cfg) {
7523         cfg.el  = s;
7524     }
7525     
7526     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7527 };
7528 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7529     
7530      
7531     enable:Roo.emptyFn,
7532     disable:Roo.emptyFn,
7533     focus:Roo.emptyFn
7534 });
7535
7536 /**
7537  * @class Roo.Toolbar.Button
7538  * @extends Roo.Button
7539  * A button that renders into a toolbar.
7540  * @constructor
7541  * Creates a new Button
7542  * @param {Object} config A standard {@link Roo.Button} config object
7543  */
7544 Roo.Toolbar.Button = function(config){
7545     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7546 };
7547 Roo.extend(Roo.Toolbar.Button, Roo.Button,
7548 {
7549     
7550     
7551     render : function(td){
7552         this.td = td;
7553         Roo.Toolbar.Button.superclass.render.call(this, td);
7554     },
7555     
7556     /**
7557      * Removes and destroys this button
7558      */
7559     destroy : function(){
7560         Roo.Toolbar.Button.superclass.destroy.call(this);
7561         this.td.parentNode.removeChild(this.td);
7562     },
7563     
7564     /**
7565      * Shows this button
7566      */
7567     show: function(){
7568         this.hidden = false;
7569         this.td.style.display = "";
7570     },
7571     
7572     /**
7573      * Hides this button
7574      */
7575     hide: function(){
7576         this.hidden = true;
7577         this.td.style.display = "none";
7578     },
7579
7580     /**
7581      * Disables this item
7582      */
7583     disable : function(){
7584         Roo.fly(this.td).addClass("x-item-disabled");
7585         this.disabled = true;
7586     },
7587
7588     /**
7589      * Enables this item
7590      */
7591     enable : function(){
7592         Roo.fly(this.td).removeClass("x-item-disabled");
7593         this.disabled = false;
7594     }
7595 });
7596 // backwards compat
7597 Roo.ToolbarButton = Roo.Toolbar.Button;
7598
7599 /**
7600  * @class Roo.Toolbar.SplitButton
7601  * @extends Roo.SplitButton
7602  * A menu button that renders into a toolbar.
7603  * @constructor
7604  * Creates a new SplitButton
7605  * @param {Object} config A standard {@link Roo.SplitButton} config object
7606  */
7607 Roo.Toolbar.SplitButton = function(config){
7608     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
7609 };
7610 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
7611     render : function(td){
7612         this.td = td;
7613         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
7614     },
7615     
7616     /**
7617      * Removes and destroys this button
7618      */
7619     destroy : function(){
7620         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
7621         this.td.parentNode.removeChild(this.td);
7622     },
7623     
7624     /**
7625      * Shows this button
7626      */
7627     show: function(){
7628         this.hidden = false;
7629         this.td.style.display = "";
7630     },
7631     
7632     /**
7633      * Hides this button
7634      */
7635     hide: function(){
7636         this.hidden = true;
7637         this.td.style.display = "none";
7638     }
7639 });
7640
7641 // backwards compat
7642 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
7643  * Based on:
7644  * Ext JS Library 1.1.1
7645  * Copyright(c) 2006-2007, Ext JS, LLC.
7646  *
7647  * Originally Released Under LGPL - original licence link has changed is not relivant.
7648  *
7649  * Fork - LGPL
7650  * <script type="text/javascript">
7651  */
7652  
7653 /**
7654  * @class Roo.PagingToolbar
7655  * @extends Roo.Toolbar
7656  * @children   Roo.Toolbar.Item Roo.form.Field
7657  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
7658  * @constructor
7659  * Create a new PagingToolbar
7660  * @param {Object} config The config object
7661  */
7662 Roo.PagingToolbar = function(el, ds, config)
7663 {
7664     // old args format still supported... - xtype is prefered..
7665     if (typeof(el) == 'object' && el.xtype) {
7666         // created from xtype...
7667         config = el;
7668         ds = el.dataSource;
7669         el = config.container;
7670     }
7671     var items = [];
7672     if (config.items) {
7673         items = config.items;
7674         config.items = [];
7675     }
7676     
7677     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
7678     this.ds = ds;
7679     this.cursor = 0;
7680     this.renderButtons(this.el);
7681     this.bind(ds);
7682     
7683     // supprot items array.
7684    
7685     Roo.each(items, function(e) {
7686         this.add(Roo.factory(e));
7687     },this);
7688     
7689 };
7690
7691 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
7692     /**
7693      * @cfg {Roo.data.Store} dataSource
7694      * The underlying data store providing the paged data
7695      */
7696     /**
7697      * @cfg {String/HTMLElement/Element} container
7698      * container The id or element that will contain the toolbar
7699      */
7700     /**
7701      * @cfg {Boolean} displayInfo
7702      * True to display the displayMsg (defaults to false)
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  * Dialog which provides adjustments for working with a layout in a Dialog.
10101  * Add your necessary layout config options to the dialog's config.<br>
10102  * Example usage (including a nested layout):
10103  * <pre><code>
10104 if(!dialog){
10105     dialog = new Roo.LayoutDialog("download-dlg", {
10106         modal: true,
10107         width:600,
10108         height:450,
10109         shadow:true,
10110         minWidth:500,
10111         minHeight:350,
10112         autoTabs:true,
10113         proxyDrag:true,
10114         // layout config merges with the dialog config
10115         center:{
10116             tabPosition: "top",
10117             alwaysShowTabs: true
10118         }
10119     });
10120     dialog.addKeyListener(27, dialog.hide, dialog);
10121     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10122     dialog.addButton("Build It!", this.getDownload, this);
10123
10124     // we can even add nested layouts
10125     var innerLayout = new Roo.BorderLayout("dl-inner", {
10126         east: {
10127             initialSize: 200,
10128             autoScroll:true,
10129             split:true
10130         },
10131         center: {
10132             autoScroll:true
10133         }
10134     });
10135     innerLayout.beginUpdate();
10136     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10137     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10138     innerLayout.endUpdate(true);
10139
10140     var layout = dialog.getLayout();
10141     layout.beginUpdate();
10142     layout.add("center", new Roo.ContentPanel("standard-panel",
10143                         {title: "Download the Source", fitToFrame:true}));
10144     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10145                {title: "Build your own roo.js"}));
10146     layout.getRegion("center").showPanel(sp);
10147     layout.endUpdate();
10148 }
10149 </code></pre>
10150     * @constructor
10151     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10152     * @param {Object} config configuration options
10153   */
10154 Roo.LayoutDialog = function(el, cfg){
10155     
10156     var config=  cfg;
10157     if (typeof(cfg) == 'undefined') {
10158         config = Roo.apply({}, el);
10159         // not sure why we use documentElement here.. - it should always be body.
10160         // IE7 borks horribly if we use documentElement.
10161         // webkit also does not like documentElement - it creates a body element...
10162         el = Roo.get( document.body || document.documentElement ).createChild();
10163         //config.autoCreate = true;
10164     }
10165     
10166     
10167     config.autoTabs = false;
10168     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10169     this.body.setStyle({overflow:"hidden", position:"relative"});
10170     this.layout = new Roo.BorderLayout(this.body.dom, config);
10171     this.layout.monitorWindowResize = false;
10172     this.el.addClass("x-dlg-auto-layout");
10173     // fix case when center region overwrites center function
10174     this.center = Roo.BasicDialog.prototype.center;
10175     this.on("show", this.layout.layout, this.layout, true);
10176     if (config.items) {
10177         var xitems = config.items;
10178         delete config.items;
10179         Roo.each(xitems, this.addxtype, this);
10180     }
10181     
10182     
10183 };
10184 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10185     
10186     
10187     /**
10188      * @cfg {Roo.LayoutRegion} east  
10189      */
10190     /**
10191      * @cfg {Roo.LayoutRegion} west
10192      */
10193     /**
10194      * @cfg {Roo.LayoutRegion} south
10195      */
10196     /**
10197      * @cfg {Roo.LayoutRegion} north
10198      */
10199     /**
10200      * @cfg {Roo.LayoutRegion} center
10201      */
10202     /**
10203      * @cfg {Roo.Button} buttons[]  Bottom buttons..
10204      */
10205     
10206     
10207     /**
10208      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10209      * @deprecated
10210      */
10211     endUpdate : function(){
10212         this.layout.endUpdate();
10213     },
10214
10215     /**
10216      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10217      *  @deprecated
10218      */
10219     beginUpdate : function(){
10220         this.layout.beginUpdate();
10221     },
10222
10223     /**
10224      * Get the BorderLayout for this dialog
10225      * @return {Roo.BorderLayout}
10226      */
10227     getLayout : function(){
10228         return this.layout;
10229     },
10230
10231     showEl : function(){
10232         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10233         if(Roo.isIE7){
10234             this.layout.layout();
10235         }
10236     },
10237
10238     // private
10239     // Use the syncHeightBeforeShow config option to control this automatically
10240     syncBodyHeight : function(){
10241         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10242         if(this.layout){this.layout.layout();}
10243     },
10244     
10245       /**
10246      * Add an xtype element (actually adds to the layout.)
10247      * @return {Object} xdata xtype object data.
10248      */
10249     
10250     addxtype : function(c) {
10251         return this.layout.addxtype(c);
10252     }
10253 });/*
10254  * Based on:
10255  * Ext JS Library 1.1.1
10256  * Copyright(c) 2006-2007, Ext JS, LLC.
10257  *
10258  * Originally Released Under LGPL - original licence link has changed is not relivant.
10259  *
10260  * Fork - LGPL
10261  * <script type="text/javascript">
10262  */
10263  
10264 /**
10265  * @class Roo.MessageBox
10266  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10267  * Example usage:
10268  *<pre><code>
10269 // Basic alert:
10270 Roo.Msg.alert('Status', 'Changes saved successfully.');
10271
10272 // Prompt for user data:
10273 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10274     if (btn == 'ok'){
10275         // process text value...
10276     }
10277 });
10278
10279 // Show a dialog using config options:
10280 Roo.Msg.show({
10281    title:'Save Changes?',
10282    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10283    buttons: Roo.Msg.YESNOCANCEL,
10284    fn: processResult,
10285    animEl: 'elId'
10286 });
10287 </code></pre>
10288  * @singleton
10289  */
10290 Roo.MessageBox = function(){
10291     var dlg, opt, mask, waitTimer;
10292     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10293     var buttons, activeTextEl, bwidth;
10294
10295     // private
10296     var handleButton = function(button){
10297         dlg.hide();
10298         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10299     };
10300
10301     // private
10302     var handleHide = function(){
10303         if(opt && opt.cls){
10304             dlg.el.removeClass(opt.cls);
10305         }
10306         if(waitTimer){
10307             Roo.TaskMgr.stop(waitTimer);
10308             waitTimer = null;
10309         }
10310     };
10311
10312     // private
10313     var updateButtons = function(b){
10314         var width = 0;
10315         if(!b){
10316             buttons["ok"].hide();
10317             buttons["cancel"].hide();
10318             buttons["yes"].hide();
10319             buttons["no"].hide();
10320             dlg.footer.dom.style.display = 'none';
10321             return width;
10322         }
10323         dlg.footer.dom.style.display = '';
10324         for(var k in buttons){
10325             if(typeof buttons[k] != "function"){
10326                 if(b[k]){
10327                     buttons[k].show();
10328                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10329                     width += buttons[k].el.getWidth()+15;
10330                 }else{
10331                     buttons[k].hide();
10332                 }
10333             }
10334         }
10335         return width;
10336     };
10337
10338     // private
10339     var handleEsc = function(d, k, e){
10340         if(opt && opt.closable !== false){
10341             dlg.hide();
10342         }
10343         if(e){
10344             e.stopEvent();
10345         }
10346     };
10347
10348     return {
10349         /**
10350          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10351          * @return {Roo.BasicDialog} The BasicDialog element
10352          */
10353         getDialog : function(){
10354            if(!dlg){
10355                 dlg = new Roo.BasicDialog("x-msg-box", {
10356                     autoCreate : true,
10357                     shadow: true,
10358                     draggable: true,
10359                     resizable:false,
10360                     constraintoviewport:false,
10361                     fixedcenter:true,
10362                     collapsible : false,
10363                     shim:true,
10364                     modal: true,
10365                     width:400, height:100,
10366                     buttonAlign:"center",
10367                     closeClick : function(){
10368                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10369                             handleButton("no");
10370                         }else{
10371                             handleButton("cancel");
10372                         }
10373                     }
10374                 });
10375                 dlg.on("hide", handleHide);
10376                 mask = dlg.mask;
10377                 dlg.addKeyListener(27, handleEsc);
10378                 buttons = {};
10379                 var bt = this.buttonText;
10380                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10381                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10382                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10383                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10384                 bodyEl = dlg.body.createChild({
10385
10386                     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>'
10387                 });
10388                 msgEl = bodyEl.dom.firstChild;
10389                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10390                 textboxEl.enableDisplayMode();
10391                 textboxEl.addKeyListener([10,13], function(){
10392                     if(dlg.isVisible() && opt && opt.buttons){
10393                         if(opt.buttons.ok){
10394                             handleButton("ok");
10395                         }else if(opt.buttons.yes){
10396                             handleButton("yes");
10397                         }
10398                     }
10399                 });
10400                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10401                 textareaEl.enableDisplayMode();
10402                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10403                 progressEl.enableDisplayMode();
10404                 var pf = progressEl.dom.firstChild;
10405                 if (pf) {
10406                     pp = Roo.get(pf.firstChild);
10407                     pp.setHeight(pf.offsetHeight);
10408                 }
10409                 
10410             }
10411             return dlg;
10412         },
10413
10414         /**
10415          * Updates the message box body text
10416          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10417          * the XHTML-compliant non-breaking space character '&amp;#160;')
10418          * @return {Roo.MessageBox} This message box
10419          */
10420         updateText : function(text){
10421             if(!dlg.isVisible() && !opt.width){
10422                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10423             }
10424             msgEl.innerHTML = text || '&#160;';
10425       
10426             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10427             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10428             var w = Math.max(
10429                     Math.min(opt.width || cw , this.maxWidth), 
10430                     Math.max(opt.minWidth || this.minWidth, bwidth)
10431             );
10432             if(opt.prompt){
10433                 activeTextEl.setWidth(w);
10434             }
10435             if(dlg.isVisible()){
10436                 dlg.fixedcenter = false;
10437             }
10438             // to big, make it scroll. = But as usual stupid IE does not support
10439             // !important..
10440             
10441             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10442                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10443                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10444             } else {
10445                 bodyEl.dom.style.height = '';
10446                 bodyEl.dom.style.overflowY = '';
10447             }
10448             if (cw > w) {
10449                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10450             } else {
10451                 bodyEl.dom.style.overflowX = '';
10452             }
10453             
10454             dlg.setContentSize(w, bodyEl.getHeight());
10455             if(dlg.isVisible()){
10456                 dlg.fixedcenter = true;
10457             }
10458             return this;
10459         },
10460
10461         /**
10462          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10463          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10464          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10465          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10466          * @return {Roo.MessageBox} This message box
10467          */
10468         updateProgress : function(value, text){
10469             if(text){
10470                 this.updateText(text);
10471             }
10472             if (pp) { // weird bug on my firefox - for some reason this is not defined
10473                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10474             }
10475             return this;
10476         },        
10477
10478         /**
10479          * Returns true if the message box is currently displayed
10480          * @return {Boolean} True if the message box is visible, else false
10481          */
10482         isVisible : function(){
10483             return dlg && dlg.isVisible();  
10484         },
10485
10486         /**
10487          * Hides the message box if it is displayed
10488          */
10489         hide : function(){
10490             if(this.isVisible()){
10491                 dlg.hide();
10492             }  
10493         },
10494
10495         /**
10496          * Displays a new message box, or reinitializes an existing message box, based on the config options
10497          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10498          * The following config object properties are supported:
10499          * <pre>
10500 Property    Type             Description
10501 ----------  ---------------  ------------------------------------------------------------------------------------
10502 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10503                                    closes (defaults to undefined)
10504 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10505                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10506 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10507                                    progress and wait dialogs will ignore this property and always hide the
10508                                    close button as they can only be closed programmatically.
10509 cls               String           A custom CSS class to apply to the message box element
10510 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10511                                    displayed (defaults to 75)
10512 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10513                                    function will be btn (the name of the button that was clicked, if applicable,
10514                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10515                                    Progress and wait dialogs will ignore this option since they do not respond to
10516                                    user actions and can only be closed programmatically, so any required function
10517                                    should be called by the same code after it closes the dialog.
10518 icon              String           A CSS class that provides a background image to be used as an icon for
10519                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10520 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10521 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10522 modal             Boolean          False to allow user interaction with the page while the message box is
10523                                    displayed (defaults to true)
10524 msg               String           A string that will replace the existing message box body text (defaults
10525                                    to the XHTML-compliant non-breaking space character '&#160;')
10526 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10527 progress          Boolean          True to display a progress bar (defaults to false)
10528 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10529 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10530 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10531 title             String           The title text
10532 value             String           The string value to set into the active textbox element if displayed
10533 wait              Boolean          True to display a progress bar (defaults to false)
10534 width             Number           The width of the dialog in pixels
10535 </pre>
10536          *
10537          * Example usage:
10538          * <pre><code>
10539 Roo.Msg.show({
10540    title: 'Address',
10541    msg: 'Please enter your address:',
10542    width: 300,
10543    buttons: Roo.MessageBox.OKCANCEL,
10544    multiline: true,
10545    fn: saveAddress,
10546    animEl: 'addAddressBtn'
10547 });
10548 </code></pre>
10549          * @param {Object} config Configuration options
10550          * @return {Roo.MessageBox} This message box
10551          */
10552         show : function(options)
10553         {
10554             
10555             // this causes nightmares if you show one dialog after another
10556             // especially on callbacks..
10557              
10558             if(this.isVisible()){
10559                 
10560                 this.hide();
10561                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10562                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10563                 Roo.log("New Dialog Message:" +  options.msg )
10564                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10565                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10566                 
10567             }
10568             var d = this.getDialog();
10569             opt = options;
10570             d.setTitle(opt.title || "&#160;");
10571             d.close.setDisplayed(opt.closable !== false);
10572             activeTextEl = textboxEl;
10573             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10574             if(opt.prompt){
10575                 if(opt.multiline){
10576                     textboxEl.hide();
10577                     textareaEl.show();
10578                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10579                         opt.multiline : this.defaultTextHeight);
10580                     activeTextEl = textareaEl;
10581                 }else{
10582                     textboxEl.show();
10583                     textareaEl.hide();
10584                 }
10585             }else{
10586                 textboxEl.hide();
10587                 textareaEl.hide();
10588             }
10589             progressEl.setDisplayed(opt.progress === true);
10590             this.updateProgress(0);
10591             activeTextEl.dom.value = opt.value || "";
10592             if(opt.prompt){
10593                 dlg.setDefaultButton(activeTextEl);
10594             }else{
10595                 var bs = opt.buttons;
10596                 var db = null;
10597                 if(bs && bs.ok){
10598                     db = buttons["ok"];
10599                 }else if(bs && bs.yes){
10600                     db = buttons["yes"];
10601                 }
10602                 dlg.setDefaultButton(db);
10603             }
10604             bwidth = updateButtons(opt.buttons);
10605             this.updateText(opt.msg);
10606             if(opt.cls){
10607                 d.el.addClass(opt.cls);
10608             }
10609             d.proxyDrag = opt.proxyDrag === true;
10610             d.modal = opt.modal !== false;
10611             d.mask = opt.modal !== false ? mask : false;
10612             if(!d.isVisible()){
10613                 // force it to the end of the z-index stack so it gets a cursor in FF
10614                 document.body.appendChild(dlg.el.dom);
10615                 d.animateTarget = null;
10616                 d.show(options.animEl);
10617             }
10618             return this;
10619         },
10620
10621         /**
10622          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
10623          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
10624          * and closing the message box when the process is complete.
10625          * @param {String} title The title bar text
10626          * @param {String} msg The message box body text
10627          * @return {Roo.MessageBox} This message box
10628          */
10629         progress : function(title, msg){
10630             this.show({
10631                 title : title,
10632                 msg : msg,
10633                 buttons: false,
10634                 progress:true,
10635                 closable:false,
10636                 minWidth: this.minProgressWidth,
10637                 modal : true
10638             });
10639             return this;
10640         },
10641
10642         /**
10643          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
10644          * If a callback function is passed it will be called after the user clicks the button, and the
10645          * id of the button that was clicked will be passed as the only parameter to the callback
10646          * (could also be the top-right close button).
10647          * @param {String} title The title bar text
10648          * @param {String} msg The message box body text
10649          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10650          * @param {Object} scope (optional) The scope of the callback function
10651          * @return {Roo.MessageBox} This message box
10652          */
10653         alert : function(title, msg, fn, scope){
10654             this.show({
10655                 title : title,
10656                 msg : msg,
10657                 buttons: this.OK,
10658                 fn: fn,
10659                 scope : scope,
10660                 modal : true
10661             });
10662             return this;
10663         },
10664
10665         /**
10666          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
10667          * interaction while waiting for a long-running process to complete that does not have defined intervals.
10668          * You are responsible for closing the message box when the process is complete.
10669          * @param {String} msg The message box body text
10670          * @param {String} title (optional) The title bar text
10671          * @return {Roo.MessageBox} This message box
10672          */
10673         wait : function(msg, title){
10674             this.show({
10675                 title : title,
10676                 msg : msg,
10677                 buttons: false,
10678                 closable:false,
10679                 progress:true,
10680                 modal:true,
10681                 width:300,
10682                 wait:true
10683             });
10684             waitTimer = Roo.TaskMgr.start({
10685                 run: function(i){
10686                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
10687                 },
10688                 interval: 1000
10689             });
10690             return this;
10691         },
10692
10693         /**
10694          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
10695          * If a callback function is passed it will be called after the user clicks either button, and the id of the
10696          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
10697          * @param {String} title The title bar text
10698          * @param {String} msg The message box body text
10699          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10700          * @param {Object} scope (optional) The scope of the callback function
10701          * @return {Roo.MessageBox} This message box
10702          */
10703         confirm : function(title, msg, fn, scope){
10704             this.show({
10705                 title : title,
10706                 msg : msg,
10707                 buttons: this.YESNO,
10708                 fn: fn,
10709                 scope : scope,
10710                 modal : true
10711             });
10712             return this;
10713         },
10714
10715         /**
10716          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
10717          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
10718          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
10719          * (could also be the top-right close button) and the text that was entered will be passed as the two
10720          * parameters to the callback.
10721          * @param {String} title The title bar text
10722          * @param {String} msg The message box body text
10723          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10724          * @param {Object} scope (optional) The scope of the callback function
10725          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
10726          * property, or the height in pixels to create the textbox (defaults to false / single-line)
10727          * @return {Roo.MessageBox} This message box
10728          */
10729         prompt : function(title, msg, fn, scope, multiline){
10730             this.show({
10731                 title : title,
10732                 msg : msg,
10733                 buttons: this.OKCANCEL,
10734                 fn: fn,
10735                 minWidth:250,
10736                 scope : scope,
10737                 prompt:true,
10738                 multiline: multiline,
10739                 modal : true
10740             });
10741             return this;
10742         },
10743
10744         /**
10745          * Button config that displays a single OK button
10746          * @type Object
10747          */
10748         OK : {ok:true},
10749         /**
10750          * Button config that displays Yes and No buttons
10751          * @type Object
10752          */
10753         YESNO : {yes:true, no:true},
10754         /**
10755          * Button config that displays OK and Cancel buttons
10756          * @type Object
10757          */
10758         OKCANCEL : {ok:true, cancel:true},
10759         /**
10760          * Button config that displays Yes, No and Cancel buttons
10761          * @type Object
10762          */
10763         YESNOCANCEL : {yes:true, no:true, cancel:true},
10764
10765         /**
10766          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
10767          * @type Number
10768          */
10769         defaultTextHeight : 75,
10770         /**
10771          * The maximum width in pixels of the message box (defaults to 600)
10772          * @type Number
10773          */
10774         maxWidth : 600,
10775         /**
10776          * The minimum width in pixels of the message box (defaults to 100)
10777          * @type Number
10778          */
10779         minWidth : 100,
10780         /**
10781          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
10782          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
10783          * @type Number
10784          */
10785         minProgressWidth : 250,
10786         /**
10787          * An object containing the default button text strings that can be overriden for localized language support.
10788          * Supported properties are: ok, cancel, yes and no.
10789          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
10790          * @type Object
10791          */
10792         buttonText : {
10793             ok : "OK",
10794             cancel : "Cancel",
10795             yes : "Yes",
10796             no : "No"
10797         }
10798     };
10799 }();
10800
10801 /**
10802  * Shorthand for {@link Roo.MessageBox}
10803  */
10804 Roo.Msg = Roo.MessageBox;/*
10805  * Based on:
10806  * Ext JS Library 1.1.1
10807  * Copyright(c) 2006-2007, Ext JS, LLC.
10808  *
10809  * Originally Released Under LGPL - original licence link has changed is not relivant.
10810  *
10811  * Fork - LGPL
10812  * <script type="text/javascript">
10813  */
10814 /**
10815  * @class Roo.QuickTips
10816  * Provides attractive and customizable tooltips for any element.
10817  * @singleton
10818  */
10819 Roo.QuickTips = function(){
10820     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
10821     var ce, bd, xy, dd;
10822     var visible = false, disabled = true, inited = false;
10823     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
10824     
10825     var onOver = function(e){
10826         if(disabled){
10827             return;
10828         }
10829         var t = e.getTarget();
10830         if(!t || t.nodeType !== 1 || t == document || t == document.body){
10831             return;
10832         }
10833         if(ce && t == ce.el){
10834             clearTimeout(hideProc);
10835             return;
10836         }
10837         if(t && tagEls[t.id]){
10838             tagEls[t.id].el = t;
10839             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
10840             return;
10841         }
10842         var ttp, et = Roo.fly(t);
10843         var ns = cfg.namespace;
10844         if(tm.interceptTitles && t.title){
10845             ttp = t.title;
10846             t.qtip = ttp;
10847             t.removeAttribute("title");
10848             e.preventDefault();
10849         }else{
10850             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
10851         }
10852         if(ttp){
10853             showProc = show.defer(tm.showDelay, tm, [{
10854                 el: t, 
10855                 text: ttp.replace(/\\n/g,'<br/>'),
10856                 width: et.getAttributeNS(ns, cfg.width),
10857                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
10858                 title: et.getAttributeNS(ns, cfg.title),
10859                     cls: et.getAttributeNS(ns, cfg.cls)
10860             }]);
10861         }
10862     };
10863     
10864     var onOut = function(e){
10865         clearTimeout(showProc);
10866         var t = e.getTarget();
10867         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
10868             hideProc = setTimeout(hide, tm.hideDelay);
10869         }
10870     };
10871     
10872     var onMove = function(e){
10873         if(disabled){
10874             return;
10875         }
10876         xy = e.getXY();
10877         xy[1] += 18;
10878         if(tm.trackMouse && ce){
10879             el.setXY(xy);
10880         }
10881     };
10882     
10883     var onDown = function(e){
10884         clearTimeout(showProc);
10885         clearTimeout(hideProc);
10886         if(!e.within(el)){
10887             if(tm.hideOnClick){
10888                 hide();
10889                 tm.disable();
10890                 tm.enable.defer(100, tm);
10891             }
10892         }
10893     };
10894     
10895     var getPad = function(){
10896         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
10897     };
10898
10899     var show = function(o){
10900         if(disabled){
10901             return;
10902         }
10903         clearTimeout(dismissProc);
10904         ce = o;
10905         if(removeCls){ // in case manually hidden
10906             el.removeClass(removeCls);
10907             removeCls = null;
10908         }
10909         if(ce.cls){
10910             el.addClass(ce.cls);
10911             removeCls = ce.cls;
10912         }
10913         if(ce.title){
10914             tipTitle.update(ce.title);
10915             tipTitle.show();
10916         }else{
10917             tipTitle.update('');
10918             tipTitle.hide();
10919         }
10920         el.dom.style.width  = tm.maxWidth+'px';
10921         //tipBody.dom.style.width = '';
10922         tipBodyText.update(o.text);
10923         var p = getPad(), w = ce.width;
10924         if(!w){
10925             var td = tipBodyText.dom;
10926             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
10927             if(aw > tm.maxWidth){
10928                 w = tm.maxWidth;
10929             }else if(aw < tm.minWidth){
10930                 w = tm.minWidth;
10931             }else{
10932                 w = aw;
10933             }
10934         }
10935         //tipBody.setWidth(w);
10936         el.setWidth(parseInt(w, 10) + p);
10937         if(ce.autoHide === false){
10938             close.setDisplayed(true);
10939             if(dd){
10940                 dd.unlock();
10941             }
10942         }else{
10943             close.setDisplayed(false);
10944             if(dd){
10945                 dd.lock();
10946             }
10947         }
10948         if(xy){
10949             el.avoidY = xy[1]-18;
10950             el.setXY(xy);
10951         }
10952         if(tm.animate){
10953             el.setOpacity(.1);
10954             el.setStyle("visibility", "visible");
10955             el.fadeIn({callback: afterShow});
10956         }else{
10957             afterShow();
10958         }
10959     };
10960     
10961     var afterShow = function(){
10962         if(ce){
10963             el.show();
10964             esc.enable();
10965             if(tm.autoDismiss && ce.autoHide !== false){
10966                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
10967             }
10968         }
10969     };
10970     
10971     var hide = function(noanim){
10972         clearTimeout(dismissProc);
10973         clearTimeout(hideProc);
10974         ce = null;
10975         if(el.isVisible()){
10976             esc.disable();
10977             if(noanim !== true && tm.animate){
10978                 el.fadeOut({callback: afterHide});
10979             }else{
10980                 afterHide();
10981             } 
10982         }
10983     };
10984     
10985     var afterHide = function(){
10986         el.hide();
10987         if(removeCls){
10988             el.removeClass(removeCls);
10989             removeCls = null;
10990         }
10991     };
10992     
10993     return {
10994         /**
10995         * @cfg {Number} minWidth
10996         * The minimum width of the quick tip (defaults to 40)
10997         */
10998        minWidth : 40,
10999         /**
11000         * @cfg {Number} maxWidth
11001         * The maximum width of the quick tip (defaults to 300)
11002         */
11003        maxWidth : 300,
11004         /**
11005         * @cfg {Boolean} interceptTitles
11006         * True to automatically use the element's DOM title value if available (defaults to false)
11007         */
11008        interceptTitles : false,
11009         /**
11010         * @cfg {Boolean} trackMouse
11011         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11012         */
11013        trackMouse : false,
11014         /**
11015         * @cfg {Boolean} hideOnClick
11016         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11017         */
11018        hideOnClick : true,
11019         /**
11020         * @cfg {Number} showDelay
11021         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11022         */
11023        showDelay : 500,
11024         /**
11025         * @cfg {Number} hideDelay
11026         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11027         */
11028        hideDelay : 200,
11029         /**
11030         * @cfg {Boolean} autoHide
11031         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11032         * Used in conjunction with hideDelay.
11033         */
11034        autoHide : true,
11035         /**
11036         * @cfg {Boolean}
11037         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11038         * (defaults to true).  Used in conjunction with autoDismissDelay.
11039         */
11040        autoDismiss : true,
11041         /**
11042         * @cfg {Number}
11043         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11044         */
11045        autoDismissDelay : 5000,
11046        /**
11047         * @cfg {Boolean} animate
11048         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11049         */
11050        animate : false,
11051
11052        /**
11053         * @cfg {String} title
11054         * Title text to display (defaults to '').  This can be any valid HTML markup.
11055         */
11056         title: '',
11057        /**
11058         * @cfg {String} text
11059         * Body text to display (defaults to '').  This can be any valid HTML markup.
11060         */
11061         text : '',
11062        /**
11063         * @cfg {String} cls
11064         * A CSS class to apply to the base quick tip element (defaults to '').
11065         */
11066         cls : '',
11067        /**
11068         * @cfg {Number} width
11069         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11070         * minWidth or maxWidth.
11071         */
11072         width : null,
11073
11074     /**
11075      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11076      * or display QuickTips in a page.
11077      */
11078        init : function(){
11079           tm = Roo.QuickTips;
11080           cfg = tm.tagConfig;
11081           if(!inited){
11082               if(!Roo.isReady){ // allow calling of init() before onReady
11083                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11084                   return;
11085               }
11086               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11087               el.fxDefaults = {stopFx: true};
11088               // maximum custom styling
11089               //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>');
11090               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>');              
11091               tipTitle = el.child('h3');
11092               tipTitle.enableDisplayMode("block");
11093               tipBody = el.child('div.x-tip-bd');
11094               tipBodyText = el.child('div.x-tip-bd-inner');
11095               //bdLeft = el.child('div.x-tip-bd-left');
11096               //bdRight = el.child('div.x-tip-bd-right');
11097               close = el.child('div.x-tip-close');
11098               close.enableDisplayMode("block");
11099               close.on("click", hide);
11100               var d = Roo.get(document);
11101               d.on("mousedown", onDown);
11102               d.on("mouseover", onOver);
11103               d.on("mouseout", onOut);
11104               d.on("mousemove", onMove);
11105               esc = d.addKeyListener(27, hide);
11106               esc.disable();
11107               if(Roo.dd.DD){
11108                   dd = el.initDD("default", null, {
11109                       onDrag : function(){
11110                           el.sync();  
11111                       }
11112                   });
11113                   dd.setHandleElId(tipTitle.id);
11114                   dd.lock();
11115               }
11116               inited = true;
11117           }
11118           this.enable(); 
11119        },
11120
11121     /**
11122      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11123      * are supported:
11124      * <pre>
11125 Property    Type                   Description
11126 ----------  ---------------------  ------------------------------------------------------------------------
11127 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11128      * </ul>
11129      * @param {Object} config The config object
11130      */
11131        register : function(config){
11132            var cs = config instanceof Array ? config : arguments;
11133            for(var i = 0, len = cs.length; i < len; i++) {
11134                var c = cs[i];
11135                var target = c.target;
11136                if(target){
11137                    if(target instanceof Array){
11138                        for(var j = 0, jlen = target.length; j < jlen; j++){
11139                            tagEls[target[j]] = c;
11140                        }
11141                    }else{
11142                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11143                    }
11144                }
11145            }
11146        },
11147
11148     /**
11149      * Removes this quick tip from its element and destroys it.
11150      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11151      */
11152        unregister : function(el){
11153            delete tagEls[Roo.id(el)];
11154        },
11155
11156     /**
11157      * Enable this quick tip.
11158      */
11159        enable : function(){
11160            if(inited && disabled){
11161                locks.pop();
11162                if(locks.length < 1){
11163                    disabled = false;
11164                }
11165            }
11166        },
11167
11168     /**
11169      * Disable this quick tip.
11170      */
11171        disable : function(){
11172           disabled = true;
11173           clearTimeout(showProc);
11174           clearTimeout(hideProc);
11175           clearTimeout(dismissProc);
11176           if(ce){
11177               hide(true);
11178           }
11179           locks.push(1);
11180        },
11181
11182     /**
11183      * Returns true if the quick tip is enabled, else false.
11184      */
11185        isEnabled : function(){
11186             return !disabled;
11187        },
11188
11189         // private
11190        tagConfig : {
11191            namespace : "roo", // was ext?? this may break..
11192            alt_namespace : "ext",
11193            attribute : "qtip",
11194            width : "width",
11195            target : "target",
11196            title : "qtitle",
11197            hide : "hide",
11198            cls : "qclass"
11199        }
11200    };
11201 }();
11202
11203 // backwards compat
11204 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11205  * Based on:
11206  * Ext JS Library 1.1.1
11207  * Copyright(c) 2006-2007, Ext JS, LLC.
11208  *
11209  * Originally Released Under LGPL - original licence link has changed is not relivant.
11210  *
11211  * Fork - LGPL
11212  * <script type="text/javascript">
11213  */
11214  
11215
11216 /**
11217  * @class Roo.tree.TreePanel
11218  * @extends Roo.data.Tree
11219  * @cfg {Roo.tree.TreeNode} root The root node
11220  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11221  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11222  * @cfg {Boolean} enableDD true to enable drag and drop
11223  * @cfg {Boolean} enableDrag true to enable just drag
11224  * @cfg {Boolean} enableDrop true to enable just drop
11225  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11226  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11227  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11228  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11229  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11230  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11231  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11232  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11233  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11234  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11235  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11236  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
11237  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
11238  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11239  * @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>
11240  * @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>
11241  * 
11242  * @constructor
11243  * @param {String/HTMLElement/Element} el The container element
11244  * @param {Object} config
11245  */
11246 Roo.tree.TreePanel = function(el, config){
11247     var root = false;
11248     var loader = false;
11249     if (config.root) {
11250         root = config.root;
11251         delete config.root;
11252     }
11253     if (config.loader) {
11254         loader = config.loader;
11255         delete config.loader;
11256     }
11257     
11258     Roo.apply(this, config);
11259     Roo.tree.TreePanel.superclass.constructor.call(this);
11260     this.el = Roo.get(el);
11261     this.el.addClass('x-tree');
11262     //console.log(root);
11263     if (root) {
11264         this.setRootNode( Roo.factory(root, Roo.tree));
11265     }
11266     if (loader) {
11267         this.loader = Roo.factory(loader, Roo.tree);
11268     }
11269    /**
11270     * Read-only. The id of the container element becomes this TreePanel's id.
11271     */
11272     this.id = this.el.id;
11273     this.addEvents({
11274         /**
11275         * @event beforeload
11276         * Fires before a node is loaded, return false to cancel
11277         * @param {Node} node The node being loaded
11278         */
11279         "beforeload" : true,
11280         /**
11281         * @event load
11282         * Fires when a node is loaded
11283         * @param {Node} node The node that was loaded
11284         */
11285         "load" : true,
11286         /**
11287         * @event textchange
11288         * Fires when the text for a node is changed
11289         * @param {Node} node The node
11290         * @param {String} text The new text
11291         * @param {String} oldText The old text
11292         */
11293         "textchange" : true,
11294         /**
11295         * @event beforeexpand
11296         * Fires before a node is expanded, return false to cancel.
11297         * @param {Node} node The node
11298         * @param {Boolean} deep
11299         * @param {Boolean} anim
11300         */
11301         "beforeexpand" : true,
11302         /**
11303         * @event beforecollapse
11304         * Fires before a node is collapsed, return false to cancel.
11305         * @param {Node} node The node
11306         * @param {Boolean} deep
11307         * @param {Boolean} anim
11308         */
11309         "beforecollapse" : true,
11310         /**
11311         * @event expand
11312         * Fires when a node is expanded
11313         * @param {Node} node The node
11314         */
11315         "expand" : true,
11316         /**
11317         * @event disabledchange
11318         * Fires when the disabled status of a node changes
11319         * @param {Node} node The node
11320         * @param {Boolean} disabled
11321         */
11322         "disabledchange" : true,
11323         /**
11324         * @event collapse
11325         * Fires when a node is collapsed
11326         * @param {Node} node The node
11327         */
11328         "collapse" : true,
11329         /**
11330         * @event beforeclick
11331         * Fires before click processing on a node. Return false to cancel the default action.
11332         * @param {Node} node The node
11333         * @param {Roo.EventObject} e The event object
11334         */
11335         "beforeclick":true,
11336         /**
11337         * @event checkchange
11338         * Fires when a node with a checkbox's checked property changes
11339         * @param {Node} this This node
11340         * @param {Boolean} checked
11341         */
11342         "checkchange":true,
11343         /**
11344         * @event click
11345         * Fires when a node is clicked
11346         * @param {Node} node The node
11347         * @param {Roo.EventObject} e The event object
11348         */
11349         "click":true,
11350         /**
11351         * @event dblclick
11352         * Fires when a node is double clicked
11353         * @param {Node} node The node
11354         * @param {Roo.EventObject} e The event object
11355         */
11356         "dblclick":true,
11357         /**
11358         * @event contextmenu
11359         * Fires when a node is right clicked
11360         * @param {Node} node The node
11361         * @param {Roo.EventObject} e The event object
11362         */
11363         "contextmenu":true,
11364         /**
11365         * @event beforechildrenrendered
11366         * Fires right before the child nodes for a node are rendered
11367         * @param {Node} node The node
11368         */
11369         "beforechildrenrendered":true,
11370         /**
11371         * @event startdrag
11372         * Fires when a node starts being dragged
11373         * @param {Roo.tree.TreePanel} this
11374         * @param {Roo.tree.TreeNode} node
11375         * @param {event} e The raw browser event
11376         */ 
11377        "startdrag" : true,
11378        /**
11379         * @event enddrag
11380         * Fires when a drag operation is complete
11381         * @param {Roo.tree.TreePanel} this
11382         * @param {Roo.tree.TreeNode} node
11383         * @param {event} e The raw browser event
11384         */
11385        "enddrag" : true,
11386        /**
11387         * @event dragdrop
11388         * Fires when a dragged node is dropped on a valid DD target
11389         * @param {Roo.tree.TreePanel} this
11390         * @param {Roo.tree.TreeNode} node
11391         * @param {DD} dd The dd it was dropped on
11392         * @param {event} e The raw browser event
11393         */
11394        "dragdrop" : true,
11395        /**
11396         * @event beforenodedrop
11397         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11398         * passed to handlers has the following properties:<br />
11399         * <ul style="padding:5px;padding-left:16px;">
11400         * <li>tree - The TreePanel</li>
11401         * <li>target - The node being targeted for the drop</li>
11402         * <li>data - The drag data from the drag source</li>
11403         * <li>point - The point of the drop - append, above or below</li>
11404         * <li>source - The drag source</li>
11405         * <li>rawEvent - Raw mouse event</li>
11406         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11407         * to be inserted by setting them on this object.</li>
11408         * <li>cancel - Set this to true to cancel the drop.</li>
11409         * </ul>
11410         * @param {Object} dropEvent
11411         */
11412        "beforenodedrop" : true,
11413        /**
11414         * @event nodedrop
11415         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11416         * passed to handlers has the following properties:<br />
11417         * <ul style="padding:5px;padding-left:16px;">
11418         * <li>tree - The TreePanel</li>
11419         * <li>target - The node being targeted for the drop</li>
11420         * <li>data - The drag data from the drag source</li>
11421         * <li>point - The point of the drop - append, above or below</li>
11422         * <li>source - The drag source</li>
11423         * <li>rawEvent - Raw mouse event</li>
11424         * <li>dropNode - Dropped node(s).</li>
11425         * </ul>
11426         * @param {Object} dropEvent
11427         */
11428        "nodedrop" : true,
11429         /**
11430         * @event nodedragover
11431         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11432         * passed to handlers has the following properties:<br />
11433         * <ul style="padding:5px;padding-left:16px;">
11434         * <li>tree - The TreePanel</li>
11435         * <li>target - The node being targeted for the drop</li>
11436         * <li>data - The drag data from the drag source</li>
11437         * <li>point - The point of the drop - append, above or below</li>
11438         * <li>source - The drag source</li>
11439         * <li>rawEvent - Raw mouse event</li>
11440         * <li>dropNode - Drop node(s) provided by the source.</li>
11441         * <li>cancel - Set this to true to signal drop not allowed.</li>
11442         * </ul>
11443         * @param {Object} dragOverEvent
11444         */
11445        "nodedragover" : true,
11446        /**
11447         * @event appendnode
11448         * Fires when append node to the tree
11449         * @param {Roo.tree.TreePanel} this
11450         * @param {Roo.tree.TreeNode} node
11451         * @param {Number} index The index of the newly appended node
11452         */
11453        "appendnode" : true
11454         
11455     });
11456     if(this.singleExpand){
11457        this.on("beforeexpand", this.restrictExpand, this);
11458     }
11459     if (this.editor) {
11460         this.editor.tree = this;
11461         this.editor = Roo.factory(this.editor, Roo.tree);
11462     }
11463     
11464     if (this.selModel) {
11465         this.selModel = Roo.factory(this.selModel, Roo.tree);
11466     }
11467    
11468 };
11469 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11470     rootVisible : true,
11471     animate: Roo.enableFx,
11472     lines : true,
11473     enableDD : false,
11474     hlDrop : Roo.enableFx,
11475   
11476     renderer: false,
11477     
11478     rendererTip: false,
11479     // private
11480     restrictExpand : function(node){
11481         var p = node.parentNode;
11482         if(p){
11483             if(p.expandedChild && p.expandedChild.parentNode == p){
11484                 p.expandedChild.collapse();
11485             }
11486             p.expandedChild = node;
11487         }
11488     },
11489
11490     // private override
11491     setRootNode : function(node){
11492         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11493         if(!this.rootVisible){
11494             node.ui = new Roo.tree.RootTreeNodeUI(node);
11495         }
11496         return node;
11497     },
11498
11499     /**
11500      * Returns the container element for this TreePanel
11501      */
11502     getEl : function(){
11503         return this.el;
11504     },
11505
11506     /**
11507      * Returns the default TreeLoader for this TreePanel
11508      */
11509     getLoader : function(){
11510         return this.loader;
11511     },
11512
11513     /**
11514      * Expand all nodes
11515      */
11516     expandAll : function(){
11517         this.root.expand(true);
11518     },
11519
11520     /**
11521      * Collapse all nodes
11522      */
11523     collapseAll : function(){
11524         this.root.collapse(true);
11525     },
11526
11527     /**
11528      * Returns the selection model used by this TreePanel
11529      */
11530     getSelectionModel : function(){
11531         if(!this.selModel){
11532             this.selModel = new Roo.tree.DefaultSelectionModel();
11533         }
11534         return this.selModel;
11535     },
11536
11537     /**
11538      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11539      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11540      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11541      * @return {Array}
11542      */
11543     getChecked : function(a, startNode){
11544         startNode = startNode || this.root;
11545         var r = [];
11546         var f = function(){
11547             if(this.attributes.checked){
11548                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11549             }
11550         }
11551         startNode.cascade(f);
11552         return r;
11553     },
11554
11555     /**
11556      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11557      * @param {String} path
11558      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11559      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11560      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11561      */
11562     expandPath : function(path, attr, callback){
11563         attr = attr || "id";
11564         var keys = path.split(this.pathSeparator);
11565         var curNode = this.root;
11566         if(curNode.attributes[attr] != keys[1]){ // invalid root
11567             if(callback){
11568                 callback(false, null);
11569             }
11570             return;
11571         }
11572         var index = 1;
11573         var f = function(){
11574             if(++index == keys.length){
11575                 if(callback){
11576                     callback(true, curNode);
11577                 }
11578                 return;
11579             }
11580             var c = curNode.findChild(attr, keys[index]);
11581             if(!c){
11582                 if(callback){
11583                     callback(false, curNode);
11584                 }
11585                 return;
11586             }
11587             curNode = c;
11588             c.expand(false, false, f);
11589         };
11590         curNode.expand(false, false, f);
11591     },
11592
11593     /**
11594      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11595      * @param {String} path
11596      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11597      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11598      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11599      */
11600     selectPath : function(path, attr, callback){
11601         attr = attr || "id";
11602         var keys = path.split(this.pathSeparator);
11603         var v = keys.pop();
11604         if(keys.length > 0){
11605             var f = function(success, node){
11606                 if(success && node){
11607                     var n = node.findChild(attr, v);
11608                     if(n){
11609                         n.select();
11610                         if(callback){
11611                             callback(true, n);
11612                         }
11613                     }else if(callback){
11614                         callback(false, n);
11615                     }
11616                 }else{
11617                     if(callback){
11618                         callback(false, n);
11619                     }
11620                 }
11621             };
11622             this.expandPath(keys.join(this.pathSeparator), attr, f);
11623         }else{
11624             this.root.select();
11625             if(callback){
11626                 callback(true, this.root);
11627             }
11628         }
11629     },
11630
11631     getTreeEl : function(){
11632         return this.el;
11633     },
11634
11635     /**
11636      * Trigger rendering of this TreePanel
11637      */
11638     render : function(){
11639         if (this.innerCt) {
11640             return this; // stop it rendering more than once!!
11641         }
11642         
11643         this.innerCt = this.el.createChild({tag:"ul",
11644                cls:"x-tree-root-ct " +
11645                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
11646
11647         if(this.containerScroll){
11648             Roo.dd.ScrollManager.register(this.el);
11649         }
11650         if((this.enableDD || this.enableDrop) && !this.dropZone){
11651            /**
11652             * The dropZone used by this tree if drop is enabled
11653             * @type Roo.tree.TreeDropZone
11654             */
11655              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
11656                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
11657            });
11658         }
11659         if((this.enableDD || this.enableDrag) && !this.dragZone){
11660            /**
11661             * The dragZone used by this tree if drag is enabled
11662             * @type Roo.tree.TreeDragZone
11663             */
11664             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
11665                ddGroup: this.ddGroup || "TreeDD",
11666                scroll: this.ddScroll
11667            });
11668         }
11669         this.getSelectionModel().init(this);
11670         if (!this.root) {
11671             Roo.log("ROOT not set in tree");
11672             return this;
11673         }
11674         this.root.render();
11675         if(!this.rootVisible){
11676             this.root.renderChildren();
11677         }
11678         return this;
11679     }
11680 });/*
11681  * Based on:
11682  * Ext JS Library 1.1.1
11683  * Copyright(c) 2006-2007, Ext JS, LLC.
11684  *
11685  * Originally Released Under LGPL - original licence link has changed is not relivant.
11686  *
11687  * Fork - LGPL
11688  * <script type="text/javascript">
11689  */
11690  
11691
11692 /**
11693  * @class Roo.tree.DefaultSelectionModel
11694  * @extends Roo.util.Observable
11695  * The default single selection for a TreePanel.
11696  * @param {Object} cfg Configuration
11697  */
11698 Roo.tree.DefaultSelectionModel = function(cfg){
11699    this.selNode = null;
11700    
11701    
11702    
11703    this.addEvents({
11704        /**
11705         * @event selectionchange
11706         * Fires when the selected node changes
11707         * @param {DefaultSelectionModel} this
11708         * @param {TreeNode} node the new selection
11709         */
11710        "selectionchange" : true,
11711
11712        /**
11713         * @event beforeselect
11714         * Fires before the selected node changes, return false to cancel the change
11715         * @param {DefaultSelectionModel} this
11716         * @param {TreeNode} node the new selection
11717         * @param {TreeNode} node the old selection
11718         */
11719        "beforeselect" : true
11720    });
11721    
11722     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
11723 };
11724
11725 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
11726     init : function(tree){
11727         this.tree = tree;
11728         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11729         tree.on("click", this.onNodeClick, this);
11730     },
11731     
11732     onNodeClick : function(node, e){
11733         if (e.ctrlKey && this.selNode == node)  {
11734             this.unselect(node);
11735             return;
11736         }
11737         this.select(node);
11738     },
11739     
11740     /**
11741      * Select a node.
11742      * @param {TreeNode} node The node to select
11743      * @return {TreeNode} The selected node
11744      */
11745     select : function(node){
11746         var last = this.selNode;
11747         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
11748             if(last){
11749                 last.ui.onSelectedChange(false);
11750             }
11751             this.selNode = node;
11752             node.ui.onSelectedChange(true);
11753             this.fireEvent("selectionchange", this, node, last);
11754         }
11755         return node;
11756     },
11757     
11758     /**
11759      * Deselect a node.
11760      * @param {TreeNode} node The node to unselect
11761      */
11762     unselect : function(node){
11763         if(this.selNode == node){
11764             this.clearSelections();
11765         }    
11766     },
11767     
11768     /**
11769      * Clear all selections
11770      */
11771     clearSelections : function(){
11772         var n = this.selNode;
11773         if(n){
11774             n.ui.onSelectedChange(false);
11775             this.selNode = null;
11776             this.fireEvent("selectionchange", this, null);
11777         }
11778         return n;
11779     },
11780     
11781     /**
11782      * Get the selected node
11783      * @return {TreeNode} The selected node
11784      */
11785     getSelectedNode : function(){
11786         return this.selNode;    
11787     },
11788     
11789     /**
11790      * Returns true if the node is selected
11791      * @param {TreeNode} node The node to check
11792      * @return {Boolean}
11793      */
11794     isSelected : function(node){
11795         return this.selNode == node;  
11796     },
11797
11798     /**
11799      * Selects the node above the selected node in the tree, intelligently walking the nodes
11800      * @return TreeNode The new selection
11801      */
11802     selectPrevious : function(){
11803         var s = this.selNode || this.lastSelNode;
11804         if(!s){
11805             return null;
11806         }
11807         var ps = s.previousSibling;
11808         if(ps){
11809             if(!ps.isExpanded() || ps.childNodes.length < 1){
11810                 return this.select(ps);
11811             } else{
11812                 var lc = ps.lastChild;
11813                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
11814                     lc = lc.lastChild;
11815                 }
11816                 return this.select(lc);
11817             }
11818         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
11819             return this.select(s.parentNode);
11820         }
11821         return null;
11822     },
11823
11824     /**
11825      * Selects the node above the selected node in the tree, intelligently walking the nodes
11826      * @return TreeNode The new selection
11827      */
11828     selectNext : function(){
11829         var s = this.selNode || this.lastSelNode;
11830         if(!s){
11831             return null;
11832         }
11833         if(s.firstChild && s.isExpanded()){
11834              return this.select(s.firstChild);
11835          }else if(s.nextSibling){
11836              return this.select(s.nextSibling);
11837          }else if(s.parentNode){
11838             var newS = null;
11839             s.parentNode.bubble(function(){
11840                 if(this.nextSibling){
11841                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
11842                     return false;
11843                 }
11844             });
11845             return newS;
11846          }
11847         return null;
11848     },
11849
11850     onKeyDown : function(e){
11851         var s = this.selNode || this.lastSelNode;
11852         // undesirable, but required
11853         var sm = this;
11854         if(!s){
11855             return;
11856         }
11857         var k = e.getKey();
11858         switch(k){
11859              case e.DOWN:
11860                  e.stopEvent();
11861                  this.selectNext();
11862              break;
11863              case e.UP:
11864                  e.stopEvent();
11865                  this.selectPrevious();
11866              break;
11867              case e.RIGHT:
11868                  e.preventDefault();
11869                  if(s.hasChildNodes()){
11870                      if(!s.isExpanded()){
11871                          s.expand();
11872                      }else if(s.firstChild){
11873                          this.select(s.firstChild, e);
11874                      }
11875                  }
11876              break;
11877              case e.LEFT:
11878                  e.preventDefault();
11879                  if(s.hasChildNodes() && s.isExpanded()){
11880                      s.collapse();
11881                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
11882                      this.select(s.parentNode, e);
11883                  }
11884              break;
11885         };
11886     }
11887 });
11888
11889 /**
11890  * @class Roo.tree.MultiSelectionModel
11891  * @extends Roo.util.Observable
11892  * Multi selection for a TreePanel.
11893  * @param {Object} cfg Configuration
11894  */
11895 Roo.tree.MultiSelectionModel = function(){
11896    this.selNodes = [];
11897    this.selMap = {};
11898    this.addEvents({
11899        /**
11900         * @event selectionchange
11901         * Fires when the selected nodes change
11902         * @param {MultiSelectionModel} this
11903         * @param {Array} nodes Array of the selected nodes
11904         */
11905        "selectionchange" : true
11906    });
11907    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
11908    
11909 };
11910
11911 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
11912     init : function(tree){
11913         this.tree = tree;
11914         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11915         tree.on("click", this.onNodeClick, this);
11916     },
11917     
11918     onNodeClick : function(node, e){
11919         this.select(node, e, e.ctrlKey);
11920     },
11921     
11922     /**
11923      * Select a node.
11924      * @param {TreeNode} node The node to select
11925      * @param {EventObject} e (optional) An event associated with the selection
11926      * @param {Boolean} keepExisting True to retain existing selections
11927      * @return {TreeNode} The selected node
11928      */
11929     select : function(node, e, keepExisting){
11930         if(keepExisting !== true){
11931             this.clearSelections(true);
11932         }
11933         if(this.isSelected(node)){
11934             this.lastSelNode = node;
11935             return node;
11936         }
11937         this.selNodes.push(node);
11938         this.selMap[node.id] = node;
11939         this.lastSelNode = node;
11940         node.ui.onSelectedChange(true);
11941         this.fireEvent("selectionchange", this, this.selNodes);
11942         return node;
11943     },
11944     
11945     /**
11946      * Deselect a node.
11947      * @param {TreeNode} node The node to unselect
11948      */
11949     unselect : function(node){
11950         if(this.selMap[node.id]){
11951             node.ui.onSelectedChange(false);
11952             var sn = this.selNodes;
11953             var index = -1;
11954             if(sn.indexOf){
11955                 index = sn.indexOf(node);
11956             }else{
11957                 for(var i = 0, len = sn.length; i < len; i++){
11958                     if(sn[i] == node){
11959                         index = i;
11960                         break;
11961                     }
11962                 }
11963             }
11964             if(index != -1){
11965                 this.selNodes.splice(index, 1);
11966             }
11967             delete this.selMap[node.id];
11968             this.fireEvent("selectionchange", this, this.selNodes);
11969         }
11970     },
11971     
11972     /**
11973      * Clear all selections
11974      */
11975     clearSelections : function(suppressEvent){
11976         var sn = this.selNodes;
11977         if(sn.length > 0){
11978             for(var i = 0, len = sn.length; i < len; i++){
11979                 sn[i].ui.onSelectedChange(false);
11980             }
11981             this.selNodes = [];
11982             this.selMap = {};
11983             if(suppressEvent !== true){
11984                 this.fireEvent("selectionchange", this, this.selNodes);
11985             }
11986         }
11987     },
11988     
11989     /**
11990      * Returns true if the node is selected
11991      * @param {TreeNode} node The node to check
11992      * @return {Boolean}
11993      */
11994     isSelected : function(node){
11995         return this.selMap[node.id] ? true : false;  
11996     },
11997     
11998     /**
11999      * Returns an array of the selected nodes
12000      * @return {Array}
12001      */
12002     getSelectedNodes : function(){
12003         return this.selNodes;    
12004     },
12005
12006     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12007
12008     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12009
12010     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12011 });/*
12012  * Based on:
12013  * Ext JS Library 1.1.1
12014  * Copyright(c) 2006-2007, Ext JS, LLC.
12015  *
12016  * Originally Released Under LGPL - original licence link has changed is not relivant.
12017  *
12018  * Fork - LGPL
12019  * <script type="text/javascript">
12020  */
12021  
12022 /**
12023  * @class Roo.tree.TreeNode
12024  * @extends Roo.data.Node
12025  * @cfg {String} text The text for this node
12026  * @cfg {Boolean} expanded true to start the node expanded
12027  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12028  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12029  * @cfg {Boolean} disabled true to start the node disabled
12030  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12031  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12032  * @cfg {String} cls A css class to be added to the node
12033  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12034  * @cfg {String} href URL of the link used for the node (defaults to #)
12035  * @cfg {String} hrefTarget target frame for the link
12036  * @cfg {String} qtip An Ext QuickTip for the node
12037  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12038  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12039  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12040  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12041  * (defaults to undefined with no checkbox rendered)
12042  * @constructor
12043  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12044  */
12045 Roo.tree.TreeNode = function(attributes){
12046     attributes = attributes || {};
12047     if(typeof attributes == "string"){
12048         attributes = {text: attributes};
12049     }
12050     this.childrenRendered = false;
12051     this.rendered = false;
12052     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12053     this.expanded = attributes.expanded === true;
12054     this.isTarget = attributes.isTarget !== false;
12055     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12056     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12057
12058     /**
12059      * Read-only. The text for this node. To change it use setText().
12060      * @type String
12061      */
12062     this.text = attributes.text;
12063     /**
12064      * True if this node is disabled.
12065      * @type Boolean
12066      */
12067     this.disabled = attributes.disabled === true;
12068
12069     this.addEvents({
12070         /**
12071         * @event textchange
12072         * Fires when the text for this node is changed
12073         * @param {Node} this This node
12074         * @param {String} text The new text
12075         * @param {String} oldText The old text
12076         */
12077         "textchange" : true,
12078         /**
12079         * @event beforeexpand
12080         * Fires before this node is expanded, return false to cancel.
12081         * @param {Node} this This node
12082         * @param {Boolean} deep
12083         * @param {Boolean} anim
12084         */
12085         "beforeexpand" : true,
12086         /**
12087         * @event beforecollapse
12088         * Fires before this node is collapsed, return false to cancel.
12089         * @param {Node} this This node
12090         * @param {Boolean} deep
12091         * @param {Boolean} anim
12092         */
12093         "beforecollapse" : true,
12094         /**
12095         * @event expand
12096         * Fires when this node is expanded
12097         * @param {Node} this This node
12098         */
12099         "expand" : true,
12100         /**
12101         * @event disabledchange
12102         * Fires when the disabled status of this node changes
12103         * @param {Node} this This node
12104         * @param {Boolean} disabled
12105         */
12106         "disabledchange" : true,
12107         /**
12108         * @event collapse
12109         * Fires when this node is collapsed
12110         * @param {Node} this This node
12111         */
12112         "collapse" : true,
12113         /**
12114         * @event beforeclick
12115         * Fires before click processing. Return false to cancel the default action.
12116         * @param {Node} this This node
12117         * @param {Roo.EventObject} e The event object
12118         */
12119         "beforeclick":true,
12120         /**
12121         * @event checkchange
12122         * Fires when a node with a checkbox's checked property changes
12123         * @param {Node} this This node
12124         * @param {Boolean} checked
12125         */
12126         "checkchange":true,
12127         /**
12128         * @event click
12129         * Fires when this node is clicked
12130         * @param {Node} this This node
12131         * @param {Roo.EventObject} e The event object
12132         */
12133         "click":true,
12134         /**
12135         * @event dblclick
12136         * Fires when this node is double clicked
12137         * @param {Node} this This node
12138         * @param {Roo.EventObject} e The event object
12139         */
12140         "dblclick":true,
12141         /**
12142         * @event contextmenu
12143         * Fires when this node is right clicked
12144         * @param {Node} this This node
12145         * @param {Roo.EventObject} e The event object
12146         */
12147         "contextmenu":true,
12148         /**
12149         * @event beforechildrenrendered
12150         * Fires right before the child nodes for this node are rendered
12151         * @param {Node} this This node
12152         */
12153         "beforechildrenrendered":true
12154     });
12155
12156     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12157
12158     /**
12159      * Read-only. The UI for this node
12160      * @type TreeNodeUI
12161      */
12162     this.ui = new uiClass(this);
12163     
12164     // finally support items[]
12165     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12166         return;
12167     }
12168     
12169     
12170     Roo.each(this.attributes.items, function(c) {
12171         this.appendChild(Roo.factory(c,Roo.Tree));
12172     }, this);
12173     delete this.attributes.items;
12174     
12175     
12176     
12177 };
12178 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12179     preventHScroll: true,
12180     /**
12181      * Returns true if this node is expanded
12182      * @return {Boolean}
12183      */
12184     isExpanded : function(){
12185         return this.expanded;
12186     },
12187
12188     /**
12189      * Returns the UI object for this node
12190      * @return {TreeNodeUI}
12191      */
12192     getUI : function(){
12193         return this.ui;
12194     },
12195
12196     // private override
12197     setFirstChild : function(node){
12198         var of = this.firstChild;
12199         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12200         if(this.childrenRendered && of && node != of){
12201             of.renderIndent(true, true);
12202         }
12203         if(this.rendered){
12204             this.renderIndent(true, true);
12205         }
12206     },
12207
12208     // private override
12209     setLastChild : function(node){
12210         var ol = this.lastChild;
12211         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12212         if(this.childrenRendered && ol && node != ol){
12213             ol.renderIndent(true, true);
12214         }
12215         if(this.rendered){
12216             this.renderIndent(true, true);
12217         }
12218     },
12219
12220     // these methods are overridden to provide lazy rendering support
12221     // private override
12222     appendChild : function()
12223     {
12224         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12225         if(node && this.childrenRendered){
12226             node.render();
12227         }
12228         this.ui.updateExpandIcon();
12229         return node;
12230     },
12231
12232     // private override
12233     removeChild : function(node){
12234         this.ownerTree.getSelectionModel().unselect(node);
12235         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12236         // if it's been rendered remove dom node
12237         if(this.childrenRendered){
12238             node.ui.remove();
12239         }
12240         if(this.childNodes.length < 1){
12241             this.collapse(false, false);
12242         }else{
12243             this.ui.updateExpandIcon();
12244         }
12245         if(!this.firstChild) {
12246             this.childrenRendered = false;
12247         }
12248         return node;
12249     },
12250
12251     // private override
12252     insertBefore : function(node, refNode){
12253         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12254         if(newNode && refNode && this.childrenRendered){
12255             node.render();
12256         }
12257         this.ui.updateExpandIcon();
12258         return newNode;
12259     },
12260
12261     /**
12262      * Sets the text for this node
12263      * @param {String} text
12264      */
12265     setText : function(text){
12266         var oldText = this.text;
12267         this.text = text;
12268         this.attributes.text = text;
12269         if(this.rendered){ // event without subscribing
12270             this.ui.onTextChange(this, text, oldText);
12271         }
12272         this.fireEvent("textchange", this, text, oldText);
12273     },
12274
12275     /**
12276      * Triggers selection of this node
12277      */
12278     select : function(){
12279         this.getOwnerTree().getSelectionModel().select(this);
12280     },
12281
12282     /**
12283      * Triggers deselection of this node
12284      */
12285     unselect : function(){
12286         this.getOwnerTree().getSelectionModel().unselect(this);
12287     },
12288
12289     /**
12290      * Returns true if this node is selected
12291      * @return {Boolean}
12292      */
12293     isSelected : function(){
12294         return this.getOwnerTree().getSelectionModel().isSelected(this);
12295     },
12296
12297     /**
12298      * Expand this node.
12299      * @param {Boolean} deep (optional) True to expand all children as well
12300      * @param {Boolean} anim (optional) false to cancel the default animation
12301      * @param {Function} callback (optional) A callback to be called when
12302      * expanding this node completes (does not wait for deep expand to complete).
12303      * Called with 1 parameter, this node.
12304      */
12305     expand : function(deep, anim, callback){
12306         if(!this.expanded){
12307             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12308                 return;
12309             }
12310             if(!this.childrenRendered){
12311                 this.renderChildren();
12312             }
12313             this.expanded = true;
12314             
12315             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12316                 this.ui.animExpand(function(){
12317                     this.fireEvent("expand", this);
12318                     if(typeof callback == "function"){
12319                         callback(this);
12320                     }
12321                     if(deep === true){
12322                         this.expandChildNodes(true);
12323                     }
12324                 }.createDelegate(this));
12325                 return;
12326             }else{
12327                 this.ui.expand();
12328                 this.fireEvent("expand", this);
12329                 if(typeof callback == "function"){
12330                     callback(this);
12331                 }
12332             }
12333         }else{
12334            if(typeof callback == "function"){
12335                callback(this);
12336            }
12337         }
12338         if(deep === true){
12339             this.expandChildNodes(true);
12340         }
12341     },
12342
12343     isHiddenRoot : function(){
12344         return this.isRoot && !this.getOwnerTree().rootVisible;
12345     },
12346
12347     /**
12348      * Collapse this node.
12349      * @param {Boolean} deep (optional) True to collapse all children as well
12350      * @param {Boolean} anim (optional) false to cancel the default animation
12351      */
12352     collapse : function(deep, anim){
12353         if(this.expanded && !this.isHiddenRoot()){
12354             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12355                 return;
12356             }
12357             this.expanded = false;
12358             if((this.getOwnerTree().animate && anim !== false) || anim){
12359                 this.ui.animCollapse(function(){
12360                     this.fireEvent("collapse", this);
12361                     if(deep === true){
12362                         this.collapseChildNodes(true);
12363                     }
12364                 }.createDelegate(this));
12365                 return;
12366             }else{
12367                 this.ui.collapse();
12368                 this.fireEvent("collapse", this);
12369             }
12370         }
12371         if(deep === true){
12372             var cs = this.childNodes;
12373             for(var i = 0, len = cs.length; i < len; i++) {
12374                 cs[i].collapse(true, false);
12375             }
12376         }
12377     },
12378
12379     // private
12380     delayedExpand : function(delay){
12381         if(!this.expandProcId){
12382             this.expandProcId = this.expand.defer(delay, this);
12383         }
12384     },
12385
12386     // private
12387     cancelExpand : function(){
12388         if(this.expandProcId){
12389             clearTimeout(this.expandProcId);
12390         }
12391         this.expandProcId = false;
12392     },
12393
12394     /**
12395      * Toggles expanded/collapsed state of the node
12396      */
12397     toggle : function(){
12398         if(this.expanded){
12399             this.collapse();
12400         }else{
12401             this.expand();
12402         }
12403     },
12404
12405     /**
12406      * Ensures all parent nodes are expanded
12407      */
12408     ensureVisible : function(callback){
12409         var tree = this.getOwnerTree();
12410         tree.expandPath(this.parentNode.getPath(), false, function(){
12411             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12412             Roo.callback(callback);
12413         }.createDelegate(this));
12414     },
12415
12416     /**
12417      * Expand all child nodes
12418      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12419      */
12420     expandChildNodes : function(deep){
12421         var cs = this.childNodes;
12422         for(var i = 0, len = cs.length; i < len; i++) {
12423                 cs[i].expand(deep);
12424         }
12425     },
12426
12427     /**
12428      * Collapse all child nodes
12429      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12430      */
12431     collapseChildNodes : function(deep){
12432         var cs = this.childNodes;
12433         for(var i = 0, len = cs.length; i < len; i++) {
12434                 cs[i].collapse(deep);
12435         }
12436     },
12437
12438     /**
12439      * Disables this node
12440      */
12441     disable : function(){
12442         this.disabled = true;
12443         this.unselect();
12444         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12445             this.ui.onDisableChange(this, true);
12446         }
12447         this.fireEvent("disabledchange", this, true);
12448     },
12449
12450     /**
12451      * Enables this node
12452      */
12453     enable : function(){
12454         this.disabled = false;
12455         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12456             this.ui.onDisableChange(this, false);
12457         }
12458         this.fireEvent("disabledchange", this, false);
12459     },
12460
12461     // private
12462     renderChildren : function(suppressEvent){
12463         if(suppressEvent !== false){
12464             this.fireEvent("beforechildrenrendered", this);
12465         }
12466         var cs = this.childNodes;
12467         for(var i = 0, len = cs.length; i < len; i++){
12468             cs[i].render(true);
12469         }
12470         this.childrenRendered = true;
12471     },
12472
12473     // private
12474     sort : function(fn, scope){
12475         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12476         if(this.childrenRendered){
12477             var cs = this.childNodes;
12478             for(var i = 0, len = cs.length; i < len; i++){
12479                 cs[i].render(true);
12480             }
12481         }
12482     },
12483
12484     // private
12485     render : function(bulkRender){
12486         this.ui.render(bulkRender);
12487         if(!this.rendered){
12488             this.rendered = true;
12489             if(this.expanded){
12490                 this.expanded = false;
12491                 this.expand(false, false);
12492             }
12493         }
12494     },
12495
12496     // private
12497     renderIndent : function(deep, refresh){
12498         if(refresh){
12499             this.ui.childIndent = null;
12500         }
12501         this.ui.renderIndent();
12502         if(deep === true && this.childrenRendered){
12503             var cs = this.childNodes;
12504             for(var i = 0, len = cs.length; i < len; i++){
12505                 cs[i].renderIndent(true, refresh);
12506             }
12507         }
12508     }
12509 });/*
12510  * Based on:
12511  * Ext JS Library 1.1.1
12512  * Copyright(c) 2006-2007, Ext JS, LLC.
12513  *
12514  * Originally Released Under LGPL - original licence link has changed is not relivant.
12515  *
12516  * Fork - LGPL
12517  * <script type="text/javascript">
12518  */
12519  
12520 /**
12521  * @class Roo.tree.AsyncTreeNode
12522  * @extends Roo.tree.TreeNode
12523  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12524  * @constructor
12525  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12526  */
12527  Roo.tree.AsyncTreeNode = function(config){
12528     this.loaded = false;
12529     this.loading = false;
12530     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12531     /**
12532     * @event beforeload
12533     * Fires before this node is loaded, return false to cancel
12534     * @param {Node} this This node
12535     */
12536     this.addEvents({'beforeload':true, 'load': true});
12537     /**
12538     * @event load
12539     * Fires when this node is loaded
12540     * @param {Node} this This node
12541     */
12542     /**
12543      * The loader used by this node (defaults to using the tree's defined loader)
12544      * @type TreeLoader
12545      * @property loader
12546      */
12547 };
12548 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12549     expand : function(deep, anim, callback){
12550         if(this.loading){ // if an async load is already running, waiting til it's done
12551             var timer;
12552             var f = function(){
12553                 if(!this.loading){ // done loading
12554                     clearInterval(timer);
12555                     this.expand(deep, anim, callback);
12556                 }
12557             }.createDelegate(this);
12558             timer = setInterval(f, 200);
12559             return;
12560         }
12561         if(!this.loaded){
12562             if(this.fireEvent("beforeload", this) === false){
12563                 return;
12564             }
12565             this.loading = true;
12566             this.ui.beforeLoad(this);
12567             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12568             if(loader){
12569                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12570                 return;
12571             }
12572         }
12573         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12574     },
12575     
12576     /**
12577      * Returns true if this node is currently loading
12578      * @return {Boolean}
12579      */
12580     isLoading : function(){
12581         return this.loading;  
12582     },
12583     
12584     loadComplete : function(deep, anim, callback){
12585         this.loading = false;
12586         this.loaded = true;
12587         this.ui.afterLoad(this);
12588         this.fireEvent("load", this);
12589         this.expand(deep, anim, callback);
12590     },
12591     
12592     /**
12593      * Returns true if this node has been loaded
12594      * @return {Boolean}
12595      */
12596     isLoaded : function(){
12597         return this.loaded;
12598     },
12599     
12600     hasChildNodes : function(){
12601         if(!this.isLeaf() && !this.loaded){
12602             return true;
12603         }else{
12604             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12605         }
12606     },
12607
12608     /**
12609      * Trigger a reload for this node
12610      * @param {Function} callback
12611      */
12612     reload : function(callback){
12613         this.collapse(false, false);
12614         while(this.firstChild){
12615             this.removeChild(this.firstChild);
12616         }
12617         this.childrenRendered = false;
12618         this.loaded = false;
12619         if(this.isHiddenRoot()){
12620             this.expanded = false;
12621         }
12622         this.expand(false, false, callback);
12623     }
12624 });/*
12625  * Based on:
12626  * Ext JS Library 1.1.1
12627  * Copyright(c) 2006-2007, Ext JS, LLC.
12628  *
12629  * Originally Released Under LGPL - original licence link has changed is not relivant.
12630  *
12631  * Fork - LGPL
12632  * <script type="text/javascript">
12633  */
12634  
12635 /**
12636  * @class Roo.tree.TreeNodeUI
12637  * @constructor
12638  * @param {Object} node The node to render
12639  * The TreeNode UI implementation is separate from the
12640  * tree implementation. Unless you are customizing the tree UI,
12641  * you should never have to use this directly.
12642  */
12643 Roo.tree.TreeNodeUI = function(node){
12644     this.node = node;
12645     this.rendered = false;
12646     this.animating = false;
12647     this.emptyIcon = Roo.BLANK_IMAGE_URL;
12648 };
12649
12650 Roo.tree.TreeNodeUI.prototype = {
12651     removeChild : function(node){
12652         if(this.rendered){
12653             this.ctNode.removeChild(node.ui.getEl());
12654         }
12655     },
12656
12657     beforeLoad : function(){
12658          this.addClass("x-tree-node-loading");
12659     },
12660
12661     afterLoad : function(){
12662          this.removeClass("x-tree-node-loading");
12663     },
12664
12665     onTextChange : function(node, text, oldText){
12666         if(this.rendered){
12667             this.textNode.innerHTML = text;
12668         }
12669     },
12670
12671     onDisableChange : function(node, state){
12672         this.disabled = state;
12673         if(state){
12674             this.addClass("x-tree-node-disabled");
12675         }else{
12676             this.removeClass("x-tree-node-disabled");
12677         }
12678     },
12679
12680     onSelectedChange : function(state){
12681         if(state){
12682             this.focus();
12683             this.addClass("x-tree-selected");
12684         }else{
12685             //this.blur();
12686             this.removeClass("x-tree-selected");
12687         }
12688     },
12689
12690     onMove : function(tree, node, oldParent, newParent, index, refNode){
12691         this.childIndent = null;
12692         if(this.rendered){
12693             var targetNode = newParent.ui.getContainer();
12694             if(!targetNode){//target not rendered
12695                 this.holder = document.createElement("div");
12696                 this.holder.appendChild(this.wrap);
12697                 return;
12698             }
12699             var insertBefore = refNode ? refNode.ui.getEl() : null;
12700             if(insertBefore){
12701                 targetNode.insertBefore(this.wrap, insertBefore);
12702             }else{
12703                 targetNode.appendChild(this.wrap);
12704             }
12705             this.node.renderIndent(true);
12706         }
12707     },
12708
12709     addClass : function(cls){
12710         if(this.elNode){
12711             Roo.fly(this.elNode).addClass(cls);
12712         }
12713     },
12714
12715     removeClass : function(cls){
12716         if(this.elNode){
12717             Roo.fly(this.elNode).removeClass(cls);
12718         }
12719     },
12720
12721     remove : function(){
12722         if(this.rendered){
12723             this.holder = document.createElement("div");
12724             this.holder.appendChild(this.wrap);
12725         }
12726     },
12727
12728     fireEvent : function(){
12729         return this.node.fireEvent.apply(this.node, arguments);
12730     },
12731
12732     initEvents : function(){
12733         this.node.on("move", this.onMove, this);
12734         var E = Roo.EventManager;
12735         var a = this.anchor;
12736
12737         var el = Roo.fly(a, '_treeui');
12738
12739         if(Roo.isOpera){ // opera render bug ignores the CSS
12740             el.setStyle("text-decoration", "none");
12741         }
12742
12743         el.on("click", this.onClick, this);
12744         el.on("dblclick", this.onDblClick, this);
12745
12746         if(this.checkbox){
12747             Roo.EventManager.on(this.checkbox,
12748                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
12749         }
12750
12751         el.on("contextmenu", this.onContextMenu, this);
12752
12753         var icon = Roo.fly(this.iconNode);
12754         icon.on("click", this.onClick, this);
12755         icon.on("dblclick", this.onDblClick, this);
12756         icon.on("contextmenu", this.onContextMenu, this);
12757         E.on(this.ecNode, "click", this.ecClick, this, true);
12758
12759         if(this.node.disabled){
12760             this.addClass("x-tree-node-disabled");
12761         }
12762         if(this.node.hidden){
12763             this.addClass("x-tree-node-disabled");
12764         }
12765         var ot = this.node.getOwnerTree();
12766         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
12767         if(dd && (!this.node.isRoot || ot.rootVisible)){
12768             Roo.dd.Registry.register(this.elNode, {
12769                 node: this.node,
12770                 handles: this.getDDHandles(),
12771                 isHandle: false
12772             });
12773         }
12774     },
12775
12776     getDDHandles : function(){
12777         return [this.iconNode, this.textNode];
12778     },
12779
12780     hide : function(){
12781         if(this.rendered){
12782             this.wrap.style.display = "none";
12783         }
12784     },
12785
12786     show : function(){
12787         if(this.rendered){
12788             this.wrap.style.display = "";
12789         }
12790     },
12791
12792     onContextMenu : function(e){
12793         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
12794             e.preventDefault();
12795             this.focus();
12796             this.fireEvent("contextmenu", this.node, e);
12797         }
12798     },
12799
12800     onClick : function(e){
12801         if(this.dropping){
12802             e.stopEvent();
12803             return;
12804         }
12805         if(this.fireEvent("beforeclick", this.node, e) !== false){
12806             if(!this.disabled && this.node.attributes.href){
12807                 this.fireEvent("click", this.node, e);
12808                 return;
12809             }
12810             e.preventDefault();
12811             if(this.disabled){
12812                 return;
12813             }
12814
12815             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
12816                 this.node.toggle();
12817             }
12818
12819             this.fireEvent("click", this.node, e);
12820         }else{
12821             e.stopEvent();
12822         }
12823     },
12824
12825     onDblClick : function(e){
12826         e.preventDefault();
12827         if(this.disabled){
12828             return;
12829         }
12830         if(this.checkbox){
12831             this.toggleCheck();
12832         }
12833         if(!this.animating && this.node.hasChildNodes()){
12834             this.node.toggle();
12835         }
12836         this.fireEvent("dblclick", this.node, e);
12837     },
12838
12839     onCheckChange : function(){
12840         var checked = this.checkbox.checked;
12841         this.node.attributes.checked = checked;
12842         this.fireEvent('checkchange', this.node, checked);
12843     },
12844
12845     ecClick : function(e){
12846         if(!this.animating && this.node.hasChildNodes()){
12847             this.node.toggle();
12848         }
12849     },
12850
12851     startDrop : function(){
12852         this.dropping = true;
12853     },
12854
12855     // delayed drop so the click event doesn't get fired on a drop
12856     endDrop : function(){
12857        setTimeout(function(){
12858            this.dropping = false;
12859        }.createDelegate(this), 50);
12860     },
12861
12862     expand : function(){
12863         this.updateExpandIcon();
12864         this.ctNode.style.display = "";
12865     },
12866
12867     focus : function(){
12868         if(!this.node.preventHScroll){
12869             try{this.anchor.focus();
12870             }catch(e){}
12871         }else if(!Roo.isIE){
12872             try{
12873                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
12874                 var l = noscroll.scrollLeft;
12875                 this.anchor.focus();
12876                 noscroll.scrollLeft = l;
12877             }catch(e){}
12878         }
12879     },
12880
12881     toggleCheck : function(value){
12882         var cb = this.checkbox;
12883         if(cb){
12884             cb.checked = (value === undefined ? !cb.checked : value);
12885         }
12886     },
12887
12888     blur : function(){
12889         try{
12890             this.anchor.blur();
12891         }catch(e){}
12892     },
12893
12894     animExpand : function(callback){
12895         var ct = Roo.get(this.ctNode);
12896         ct.stopFx();
12897         if(!this.node.hasChildNodes()){
12898             this.updateExpandIcon();
12899             this.ctNode.style.display = "";
12900             Roo.callback(callback);
12901             return;
12902         }
12903         this.animating = true;
12904         this.updateExpandIcon();
12905
12906         ct.slideIn('t', {
12907            callback : function(){
12908                this.animating = false;
12909                Roo.callback(callback);
12910             },
12911             scope: this,
12912             duration: this.node.ownerTree.duration || .25
12913         });
12914     },
12915
12916     highlight : function(){
12917         var tree = this.node.getOwnerTree();
12918         Roo.fly(this.wrap).highlight(
12919             tree.hlColor || "C3DAF9",
12920             {endColor: tree.hlBaseColor}
12921         );
12922     },
12923
12924     collapse : function(){
12925         this.updateExpandIcon();
12926         this.ctNode.style.display = "none";
12927     },
12928
12929     animCollapse : function(callback){
12930         var ct = Roo.get(this.ctNode);
12931         ct.enableDisplayMode('block');
12932         ct.stopFx();
12933
12934         this.animating = true;
12935         this.updateExpandIcon();
12936
12937         ct.slideOut('t', {
12938             callback : function(){
12939                this.animating = false;
12940                Roo.callback(callback);
12941             },
12942             scope: this,
12943             duration: this.node.ownerTree.duration || .25
12944         });
12945     },
12946
12947     getContainer : function(){
12948         return this.ctNode;
12949     },
12950
12951     getEl : function(){
12952         return this.wrap;
12953     },
12954
12955     appendDDGhost : function(ghostNode){
12956         ghostNode.appendChild(this.elNode.cloneNode(true));
12957     },
12958
12959     getDDRepairXY : function(){
12960         return Roo.lib.Dom.getXY(this.iconNode);
12961     },
12962
12963     onRender : function(){
12964         this.render();
12965     },
12966
12967     render : function(bulkRender){
12968         var n = this.node, a = n.attributes;
12969         var targetNode = n.parentNode ?
12970               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
12971
12972         if(!this.rendered){
12973             this.rendered = true;
12974
12975             this.renderElements(n, a, targetNode, bulkRender);
12976
12977             if(a.qtip){
12978                if(this.textNode.setAttributeNS){
12979                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
12980                    if(a.qtipTitle){
12981                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
12982                    }
12983                }else{
12984                    this.textNode.setAttribute("ext:qtip", a.qtip);
12985                    if(a.qtipTitle){
12986                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
12987                    }
12988                }
12989             }else if(a.qtipCfg){
12990                 a.qtipCfg.target = Roo.id(this.textNode);
12991                 Roo.QuickTips.register(a.qtipCfg);
12992             }
12993             this.initEvents();
12994             if(!this.node.expanded){
12995                 this.updateExpandIcon();
12996             }
12997         }else{
12998             if(bulkRender === true) {
12999                 targetNode.appendChild(this.wrap);
13000             }
13001         }
13002     },
13003
13004     renderElements : function(n, a, targetNode, bulkRender)
13005     {
13006         // add some indent caching, this helps performance when rendering a large tree
13007         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13008         var t = n.getOwnerTree();
13009         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13010         if (typeof(n.attributes.html) != 'undefined') {
13011             txt = n.attributes.html;
13012         }
13013         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
13014         var cb = typeof a.checked == 'boolean';
13015         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13016         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13017             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13018             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13019             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13020             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13021             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13022              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13023                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13024             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13025             "</li>"];
13026
13027         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13028             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13029                                 n.nextSibling.ui.getEl(), buf.join(""));
13030         }else{
13031             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13032         }
13033
13034         this.elNode = this.wrap.childNodes[0];
13035         this.ctNode = this.wrap.childNodes[1];
13036         var cs = this.elNode.childNodes;
13037         this.indentNode = cs[0];
13038         this.ecNode = cs[1];
13039         this.iconNode = cs[2];
13040         var index = 3;
13041         if(cb){
13042             this.checkbox = cs[3];
13043             index++;
13044         }
13045         this.anchor = cs[index];
13046         this.textNode = cs[index].firstChild;
13047     },
13048
13049     getAnchor : function(){
13050         return this.anchor;
13051     },
13052
13053     getTextEl : function(){
13054         return this.textNode;
13055     },
13056
13057     getIconEl : function(){
13058         return this.iconNode;
13059     },
13060
13061     isChecked : function(){
13062         return this.checkbox ? this.checkbox.checked : false;
13063     },
13064
13065     updateExpandIcon : function(){
13066         if(this.rendered){
13067             var n = this.node, c1, c2;
13068             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13069             var hasChild = n.hasChildNodes();
13070             if(hasChild){
13071                 if(n.expanded){
13072                     cls += "-minus";
13073                     c1 = "x-tree-node-collapsed";
13074                     c2 = "x-tree-node-expanded";
13075                 }else{
13076                     cls += "-plus";
13077                     c1 = "x-tree-node-expanded";
13078                     c2 = "x-tree-node-collapsed";
13079                 }
13080                 if(this.wasLeaf){
13081                     this.removeClass("x-tree-node-leaf");
13082                     this.wasLeaf = false;
13083                 }
13084                 if(this.c1 != c1 || this.c2 != c2){
13085                     Roo.fly(this.elNode).replaceClass(c1, c2);
13086                     this.c1 = c1; this.c2 = c2;
13087                 }
13088             }else{
13089                 // this changes non-leafs into leafs if they have no children.
13090                 // it's not very rational behaviour..
13091                 
13092                 if(!this.wasLeaf && this.node.leaf){
13093                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13094                     delete this.c1;
13095                     delete this.c2;
13096                     this.wasLeaf = true;
13097                 }
13098             }
13099             var ecc = "x-tree-ec-icon "+cls;
13100             if(this.ecc != ecc){
13101                 this.ecNode.className = ecc;
13102                 this.ecc = ecc;
13103             }
13104         }
13105     },
13106
13107     getChildIndent : function(){
13108         if(!this.childIndent){
13109             var buf = [];
13110             var p = this.node;
13111             while(p){
13112                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13113                     if(!p.isLast()) {
13114                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13115                     } else {
13116                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13117                     }
13118                 }
13119                 p = p.parentNode;
13120             }
13121             this.childIndent = buf.join("");
13122         }
13123         return this.childIndent;
13124     },
13125
13126     renderIndent : function(){
13127         if(this.rendered){
13128             var indent = "";
13129             var p = this.node.parentNode;
13130             if(p){
13131                 indent = p.ui.getChildIndent();
13132             }
13133             if(this.indentMarkup != indent){ // don't rerender if not required
13134                 this.indentNode.innerHTML = indent;
13135                 this.indentMarkup = indent;
13136             }
13137             this.updateExpandIcon();
13138         }
13139     }
13140 };
13141
13142 Roo.tree.RootTreeNodeUI = function(){
13143     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13144 };
13145 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13146     render : function(){
13147         if(!this.rendered){
13148             var targetNode = this.node.ownerTree.innerCt.dom;
13149             this.node.expanded = true;
13150             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13151             this.wrap = this.ctNode = targetNode.firstChild;
13152         }
13153     },
13154     collapse : function(){
13155     },
13156     expand : function(){
13157     }
13158 });/*
13159  * Based on:
13160  * Ext JS Library 1.1.1
13161  * Copyright(c) 2006-2007, Ext JS, LLC.
13162  *
13163  * Originally Released Under LGPL - original licence link has changed is not relivant.
13164  *
13165  * Fork - LGPL
13166  * <script type="text/javascript">
13167  */
13168 /**
13169  * @class Roo.tree.TreeLoader
13170  * @extends Roo.util.Observable
13171  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13172  * nodes from a specified URL. The response must be a javascript Array definition
13173  * who's elements are node definition objects. eg:
13174  * <pre><code>
13175 {  success : true,
13176    data :      [
13177    
13178     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13179     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13180     ]
13181 }
13182
13183
13184 </code></pre>
13185  * <br><br>
13186  * The old style respose with just an array is still supported, but not recommended.
13187  * <br><br>
13188  *
13189  * A server request is sent, and child nodes are loaded only when a node is expanded.
13190  * The loading node's id is passed to the server under the parameter name "node" to
13191  * enable the server to produce the correct child nodes.
13192  * <br><br>
13193  * To pass extra parameters, an event handler may be attached to the "beforeload"
13194  * event, and the parameters specified in the TreeLoader's baseParams property:
13195  * <pre><code>
13196     myTreeLoader.on("beforeload", function(treeLoader, node) {
13197         this.baseParams.category = node.attributes.category;
13198     }, this);
13199     
13200 </code></pre>
13201  *
13202  * This would pass an HTTP parameter called "category" to the server containing
13203  * the value of the Node's "category" attribute.
13204  * @constructor
13205  * Creates a new Treeloader.
13206  * @param {Object} config A config object containing config properties.
13207  */
13208 Roo.tree.TreeLoader = function(config){
13209     this.baseParams = {};
13210     this.requestMethod = "POST";
13211     Roo.apply(this, config);
13212
13213     this.addEvents({
13214     
13215         /**
13216          * @event beforeload
13217          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13218          * @param {Object} This TreeLoader object.
13219          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13220          * @param {Object} callback The callback function specified in the {@link #load} call.
13221          */
13222         beforeload : true,
13223         /**
13224          * @event load
13225          * Fires when the node has been successfuly loaded.
13226          * @param {Object} This TreeLoader object.
13227          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13228          * @param {Object} response The response object containing the data from the server.
13229          */
13230         load : true,
13231         /**
13232          * @event loadexception
13233          * Fires if the network request failed.
13234          * @param {Object} This TreeLoader object.
13235          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13236          * @param {Object} response The response object containing the data from the server.
13237          */
13238         loadexception : true,
13239         /**
13240          * @event create
13241          * Fires before a node is created, enabling you to return custom Node types 
13242          * @param {Object} This TreeLoader object.
13243          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13244          */
13245         create : true
13246     });
13247
13248     Roo.tree.TreeLoader.superclass.constructor.call(this);
13249 };
13250
13251 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13252     /**
13253     * @cfg {String} dataUrl The URL from which to request a Json string which
13254     * specifies an array of node definition object representing the child nodes
13255     * to be loaded.
13256     */
13257     /**
13258     * @cfg {String} requestMethod either GET or POST
13259     * defaults to POST (due to BC)
13260     * to be loaded.
13261     */
13262     /**
13263     * @cfg {Object} baseParams (optional) An object containing properties which
13264     * specify HTTP parameters to be passed to each request for child nodes.
13265     */
13266     /**
13267     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13268     * created by this loader. If the attributes sent by the server have an attribute in this object,
13269     * they take priority.
13270     */
13271     /**
13272     * @cfg {Object} uiProviders (optional) An object containing properties which
13273     * 
13274     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13275     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13276     * <i>uiProvider</i> attribute of a returned child node is a string rather
13277     * than a reference to a TreeNodeUI implementation, this that string value
13278     * is used as a property name in the uiProviders object. You can define the provider named
13279     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13280     */
13281     uiProviders : {},
13282
13283     /**
13284     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13285     * child nodes before loading.
13286     */
13287     clearOnLoad : true,
13288
13289     /**
13290     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13291     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13292     * Grid query { data : [ .....] }
13293     */
13294     
13295     root : false,
13296      /**
13297     * @cfg {String} queryParam (optional) 
13298     * Name of the query as it will be passed on the querystring (defaults to 'node')
13299     * eg. the request will be ?node=[id]
13300     */
13301     
13302     
13303     queryParam: false,
13304     
13305     /**
13306      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13307      * This is called automatically when a node is expanded, but may be used to reload
13308      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13309      * @param {Roo.tree.TreeNode} node
13310      * @param {Function} callback
13311      */
13312     load : function(node, callback){
13313         if(this.clearOnLoad){
13314             while(node.firstChild){
13315                 node.removeChild(node.firstChild);
13316             }
13317         }
13318         if(node.attributes.children){ // preloaded json children
13319             var cs = node.attributes.children;
13320             for(var i = 0, len = cs.length; i < len; i++){
13321                 node.appendChild(this.createNode(cs[i]));
13322             }
13323             if(typeof callback == "function"){
13324                 callback();
13325             }
13326         }else if(this.dataUrl){
13327             this.requestData(node, callback);
13328         }
13329     },
13330
13331     getParams: function(node){
13332         var buf = [], bp = this.baseParams;
13333         for(var key in bp){
13334             if(typeof bp[key] != "function"){
13335                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13336             }
13337         }
13338         var n = this.queryParam === false ? 'node' : this.queryParam;
13339         buf.push(n + "=", encodeURIComponent(node.id));
13340         return buf.join("");
13341     },
13342
13343     requestData : function(node, callback){
13344         if(this.fireEvent("beforeload", this, node, callback) !== false){
13345             this.transId = Roo.Ajax.request({
13346                 method:this.requestMethod,
13347                 url: this.dataUrl||this.url,
13348                 success: this.handleResponse,
13349                 failure: this.handleFailure,
13350                 scope: this,
13351                 argument: {callback: callback, node: node},
13352                 params: this.getParams(node)
13353             });
13354         }else{
13355             // if the load is cancelled, make sure we notify
13356             // the node that we are done
13357             if(typeof callback == "function"){
13358                 callback();
13359             }
13360         }
13361     },
13362
13363     isLoading : function(){
13364         return this.transId ? true : false;
13365     },
13366
13367     abort : function(){
13368         if(this.isLoading()){
13369             Roo.Ajax.abort(this.transId);
13370         }
13371     },
13372
13373     // private
13374     createNode : function(attr)
13375     {
13376         // apply baseAttrs, nice idea Corey!
13377         if(this.baseAttrs){
13378             Roo.applyIf(attr, this.baseAttrs);
13379         }
13380         if(this.applyLoader !== false){
13381             attr.loader = this;
13382         }
13383         // uiProvider = depreciated..
13384         
13385         if(typeof(attr.uiProvider) == 'string'){
13386            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13387                 /**  eval:var:attr */ eval(attr.uiProvider);
13388         }
13389         if(typeof(this.uiProviders['default']) != 'undefined') {
13390             attr.uiProvider = this.uiProviders['default'];
13391         }
13392         
13393         this.fireEvent('create', this, attr);
13394         
13395         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13396         return(attr.leaf ?
13397                         new Roo.tree.TreeNode(attr) :
13398                         new Roo.tree.AsyncTreeNode(attr));
13399     },
13400
13401     processResponse : function(response, node, callback)
13402     {
13403         var json = response.responseText;
13404         try {
13405             
13406             var o = Roo.decode(json);
13407             
13408             if (this.root === false && typeof(o.success) != undefined) {
13409                 this.root = 'data'; // the default behaviour for list like data..
13410                 }
13411                 
13412             if (this.root !== false &&  !o.success) {
13413                 // it's a failure condition.
13414                 var a = response.argument;
13415                 this.fireEvent("loadexception", this, a.node, response);
13416                 Roo.log("Load failed - should have a handler really");
13417                 return;
13418             }
13419             
13420             
13421             
13422             if (this.root !== false) {
13423                  o = o[this.root];
13424             }
13425             
13426             for(var i = 0, len = o.length; i < len; i++){
13427                 var n = this.createNode(o[i]);
13428                 if(n){
13429                     node.appendChild(n);
13430                 }
13431             }
13432             if(typeof callback == "function"){
13433                 callback(this, node);
13434             }
13435         }catch(e){
13436             this.handleFailure(response);
13437         }
13438     },
13439
13440     handleResponse : function(response){
13441         this.transId = false;
13442         var a = response.argument;
13443         this.processResponse(response, a.node, a.callback);
13444         this.fireEvent("load", this, a.node, response);
13445     },
13446
13447     handleFailure : function(response)
13448     {
13449         // should handle failure better..
13450         this.transId = false;
13451         var a = response.argument;
13452         this.fireEvent("loadexception", this, a.node, response);
13453         if(typeof a.callback == "function"){
13454             a.callback(this, a.node);
13455         }
13456     }
13457 });/*
13458  * Based on:
13459  * Ext JS Library 1.1.1
13460  * Copyright(c) 2006-2007, Ext JS, LLC.
13461  *
13462  * Originally Released Under LGPL - original licence link has changed is not relivant.
13463  *
13464  * Fork - LGPL
13465  * <script type="text/javascript">
13466  */
13467
13468 /**
13469 * @class Roo.tree.TreeFilter
13470 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13471 * @param {TreePanel} tree
13472 * @param {Object} config (optional)
13473  */
13474 Roo.tree.TreeFilter = function(tree, config){
13475     this.tree = tree;
13476     this.filtered = {};
13477     Roo.apply(this, config);
13478 };
13479
13480 Roo.tree.TreeFilter.prototype = {
13481     clearBlank:false,
13482     reverse:false,
13483     autoClear:false,
13484     remove:false,
13485
13486      /**
13487      * Filter the data by a specific attribute.
13488      * @param {String/RegExp} value Either string that the attribute value
13489      * should start with or a RegExp to test against the attribute
13490      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13491      * @param {TreeNode} startNode (optional) The node to start the filter at.
13492      */
13493     filter : function(value, attr, startNode){
13494         attr = attr || "text";
13495         var f;
13496         if(typeof value == "string"){
13497             var vlen = value.length;
13498             // auto clear empty filter
13499             if(vlen == 0 && this.clearBlank){
13500                 this.clear();
13501                 return;
13502             }
13503             value = value.toLowerCase();
13504             f = function(n){
13505                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13506             };
13507         }else if(value.exec){ // regex?
13508             f = function(n){
13509                 return value.test(n.attributes[attr]);
13510             };
13511         }else{
13512             throw 'Illegal filter type, must be string or regex';
13513         }
13514         this.filterBy(f, null, startNode);
13515         },
13516
13517     /**
13518      * Filter by a function. The passed function will be called with each
13519      * node in the tree (or from the startNode). If the function returns true, the node is kept
13520      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13521      * @param {Function} fn The filter function
13522      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13523      */
13524     filterBy : function(fn, scope, startNode){
13525         startNode = startNode || this.tree.root;
13526         if(this.autoClear){
13527             this.clear();
13528         }
13529         var af = this.filtered, rv = this.reverse;
13530         var f = function(n){
13531             if(n == startNode){
13532                 return true;
13533             }
13534             if(af[n.id]){
13535                 return false;
13536             }
13537             var m = fn.call(scope || n, n);
13538             if(!m || rv){
13539                 af[n.id] = n;
13540                 n.ui.hide();
13541                 return false;
13542             }
13543             return true;
13544         };
13545         startNode.cascade(f);
13546         if(this.remove){
13547            for(var id in af){
13548                if(typeof id != "function"){
13549                    var n = af[id];
13550                    if(n && n.parentNode){
13551                        n.parentNode.removeChild(n);
13552                    }
13553                }
13554            }
13555         }
13556     },
13557
13558     /**
13559      * Clears the current filter. Note: with the "remove" option
13560      * set a filter cannot be cleared.
13561      */
13562     clear : function(){
13563         var t = this.tree;
13564         var af = this.filtered;
13565         for(var id in af){
13566             if(typeof id != "function"){
13567                 var n = af[id];
13568                 if(n){
13569                     n.ui.show();
13570                 }
13571             }
13572         }
13573         this.filtered = {};
13574     }
13575 };
13576 /*
13577  * Based on:
13578  * Ext JS Library 1.1.1
13579  * Copyright(c) 2006-2007, Ext JS, LLC.
13580  *
13581  * Originally Released Under LGPL - original licence link has changed is not relivant.
13582  *
13583  * Fork - LGPL
13584  * <script type="text/javascript">
13585  */
13586  
13587
13588 /**
13589  * @class Roo.tree.TreeSorter
13590  * Provides sorting of nodes in a TreePanel
13591  * 
13592  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13593  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13594  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13595  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13596  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13597  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13598  * @constructor
13599  * @param {TreePanel} tree
13600  * @param {Object} config
13601  */
13602 Roo.tree.TreeSorter = function(tree, config){
13603     Roo.apply(this, config);
13604     tree.on("beforechildrenrendered", this.doSort, this);
13605     tree.on("append", this.updateSort, this);
13606     tree.on("insert", this.updateSort, this);
13607     
13608     var dsc = this.dir && this.dir.toLowerCase() == "desc";
13609     var p = this.property || "text";
13610     var sortType = this.sortType;
13611     var fs = this.folderSort;
13612     var cs = this.caseSensitive === true;
13613     var leafAttr = this.leafAttr || 'leaf';
13614
13615     this.sortFn = function(n1, n2){
13616         if(fs){
13617             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13618                 return 1;
13619             }
13620             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13621                 return -1;
13622             }
13623         }
13624         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13625         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13626         if(v1 < v2){
13627                         return dsc ? +1 : -1;
13628                 }else if(v1 > v2){
13629                         return dsc ? -1 : +1;
13630         }else{
13631                 return 0;
13632         }
13633     };
13634 };
13635
13636 Roo.tree.TreeSorter.prototype = {
13637     doSort : function(node){
13638         node.sort(this.sortFn);
13639     },
13640     
13641     compareNodes : function(n1, n2){
13642         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
13643     },
13644     
13645     updateSort : function(tree, node){
13646         if(node.childrenRendered){
13647             this.doSort.defer(1, this, [node]);
13648         }
13649     }
13650 };/*
13651  * Based on:
13652  * Ext JS Library 1.1.1
13653  * Copyright(c) 2006-2007, Ext JS, LLC.
13654  *
13655  * Originally Released Under LGPL - original licence link has changed is not relivant.
13656  *
13657  * Fork - LGPL
13658  * <script type="text/javascript">
13659  */
13660
13661 if(Roo.dd.DropZone){
13662     
13663 Roo.tree.TreeDropZone = function(tree, config){
13664     this.allowParentInsert = false;
13665     this.allowContainerDrop = false;
13666     this.appendOnly = false;
13667     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
13668     this.tree = tree;
13669     this.lastInsertClass = "x-tree-no-status";
13670     this.dragOverData = {};
13671 };
13672
13673 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
13674     ddGroup : "TreeDD",
13675     scroll:  true,
13676     
13677     expandDelay : 1000,
13678     
13679     expandNode : function(node){
13680         if(node.hasChildNodes() && !node.isExpanded()){
13681             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
13682         }
13683     },
13684     
13685     queueExpand : function(node){
13686         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
13687     },
13688     
13689     cancelExpand : function(){
13690         if(this.expandProcId){
13691             clearTimeout(this.expandProcId);
13692             this.expandProcId = false;
13693         }
13694     },
13695     
13696     isValidDropPoint : function(n, pt, dd, e, data){
13697         if(!n || !data){ return false; }
13698         var targetNode = n.node;
13699         var dropNode = data.node;
13700         // default drop rules
13701         if(!(targetNode && targetNode.isTarget && pt)){
13702             return false;
13703         }
13704         if(pt == "append" && targetNode.allowChildren === false){
13705             return false;
13706         }
13707         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
13708             return false;
13709         }
13710         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
13711             return false;
13712         }
13713         // reuse the object
13714         var overEvent = this.dragOverData;
13715         overEvent.tree = this.tree;
13716         overEvent.target = targetNode;
13717         overEvent.data = data;
13718         overEvent.point = pt;
13719         overEvent.source = dd;
13720         overEvent.rawEvent = e;
13721         overEvent.dropNode = dropNode;
13722         overEvent.cancel = false;  
13723         var result = this.tree.fireEvent("nodedragover", overEvent);
13724         return overEvent.cancel === false && result !== false;
13725     },
13726     
13727     getDropPoint : function(e, n, dd)
13728     {
13729         var tn = n.node;
13730         if(tn.isRoot){
13731             return tn.allowChildren !== false ? "append" : false; // always append for root
13732         }
13733         var dragEl = n.ddel;
13734         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
13735         var y = Roo.lib.Event.getPageY(e);
13736         //var noAppend = tn.allowChildren === false || tn.isLeaf();
13737         
13738         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
13739         var noAppend = tn.allowChildren === false;
13740         if(this.appendOnly || tn.parentNode.allowChildren === false){
13741             return noAppend ? false : "append";
13742         }
13743         var noBelow = false;
13744         if(!this.allowParentInsert){
13745             noBelow = tn.hasChildNodes() && tn.isExpanded();
13746         }
13747         var q = (b - t) / (noAppend ? 2 : 3);
13748         if(y >= t && y < (t + q)){
13749             return "above";
13750         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
13751             return "below";
13752         }else{
13753             return "append";
13754         }
13755     },
13756     
13757     onNodeEnter : function(n, dd, e, data)
13758     {
13759         this.cancelExpand();
13760     },
13761     
13762     onNodeOver : function(n, dd, e, data)
13763     {
13764        
13765         var pt = this.getDropPoint(e, n, dd);
13766         var node = n.node;
13767         
13768         // auto node expand check
13769         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
13770             this.queueExpand(node);
13771         }else if(pt != "append"){
13772             this.cancelExpand();
13773         }
13774         
13775         // set the insert point style on the target node
13776         var returnCls = this.dropNotAllowed;
13777         if(this.isValidDropPoint(n, pt, dd, e, data)){
13778            if(pt){
13779                var el = n.ddel;
13780                var cls;
13781                if(pt == "above"){
13782                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
13783                    cls = "x-tree-drag-insert-above";
13784                }else if(pt == "below"){
13785                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
13786                    cls = "x-tree-drag-insert-below";
13787                }else{
13788                    returnCls = "x-tree-drop-ok-append";
13789                    cls = "x-tree-drag-append";
13790                }
13791                if(this.lastInsertClass != cls){
13792                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
13793                    this.lastInsertClass = cls;
13794                }
13795            }
13796        }
13797        return returnCls;
13798     },
13799     
13800     onNodeOut : function(n, dd, e, data){
13801         
13802         this.cancelExpand();
13803         this.removeDropIndicators(n);
13804     },
13805     
13806     onNodeDrop : function(n, dd, e, data){
13807         var point = this.getDropPoint(e, n, dd);
13808         var targetNode = n.node;
13809         targetNode.ui.startDrop();
13810         if(!this.isValidDropPoint(n, point, dd, e, data)){
13811             targetNode.ui.endDrop();
13812             return false;
13813         }
13814         // first try to find the drop node
13815         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
13816         var dropEvent = {
13817             tree : this.tree,
13818             target: targetNode,
13819             data: data,
13820             point: point,
13821             source: dd,
13822             rawEvent: e,
13823             dropNode: dropNode,
13824             cancel: !dropNode   
13825         };
13826         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
13827         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
13828             targetNode.ui.endDrop();
13829             return false;
13830         }
13831         // allow target changing
13832         targetNode = dropEvent.target;
13833         if(point == "append" && !targetNode.isExpanded()){
13834             targetNode.expand(false, null, function(){
13835                 this.completeDrop(dropEvent);
13836             }.createDelegate(this));
13837         }else{
13838             this.completeDrop(dropEvent);
13839         }
13840         return true;
13841     },
13842     
13843     completeDrop : function(de){
13844         var ns = de.dropNode, p = de.point, t = de.target;
13845         if(!(ns instanceof Array)){
13846             ns = [ns];
13847         }
13848         var n;
13849         for(var i = 0, len = ns.length; i < len; i++){
13850             n = ns[i];
13851             if(p == "above"){
13852                 t.parentNode.insertBefore(n, t);
13853             }else if(p == "below"){
13854                 t.parentNode.insertBefore(n, t.nextSibling);
13855             }else{
13856                 t.appendChild(n);
13857             }
13858         }
13859         n.ui.focus();
13860         if(this.tree.hlDrop){
13861             n.ui.highlight();
13862         }
13863         t.ui.endDrop();
13864         this.tree.fireEvent("nodedrop", de);
13865     },
13866     
13867     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
13868         if(this.tree.hlDrop){
13869             dropNode.ui.focus();
13870             dropNode.ui.highlight();
13871         }
13872         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
13873     },
13874     
13875     getTree : function(){
13876         return this.tree;
13877     },
13878     
13879     removeDropIndicators : function(n){
13880         if(n && n.ddel){
13881             var el = n.ddel;
13882             Roo.fly(el).removeClass([
13883                     "x-tree-drag-insert-above",
13884                     "x-tree-drag-insert-below",
13885                     "x-tree-drag-append"]);
13886             this.lastInsertClass = "_noclass";
13887         }
13888     },
13889     
13890     beforeDragDrop : function(target, e, id){
13891         this.cancelExpand();
13892         return true;
13893     },
13894     
13895     afterRepair : function(data){
13896         if(data && Roo.enableFx){
13897             data.node.ui.highlight();
13898         }
13899         this.hideProxy();
13900     } 
13901     
13902 });
13903
13904 }
13905 /*
13906  * Based on:
13907  * Ext JS Library 1.1.1
13908  * Copyright(c) 2006-2007, Ext JS, LLC.
13909  *
13910  * Originally Released Under LGPL - original licence link has changed is not relivant.
13911  *
13912  * Fork - LGPL
13913  * <script type="text/javascript">
13914  */
13915  
13916
13917 if(Roo.dd.DragZone){
13918 Roo.tree.TreeDragZone = function(tree, config){
13919     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
13920     this.tree = tree;
13921 };
13922
13923 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
13924     ddGroup : "TreeDD",
13925    
13926     onBeforeDrag : function(data, e){
13927         var n = data.node;
13928         return n && n.draggable && !n.disabled;
13929     },
13930      
13931     
13932     onInitDrag : function(e){
13933         var data = this.dragData;
13934         this.tree.getSelectionModel().select(data.node);
13935         this.proxy.update("");
13936         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
13937         this.tree.fireEvent("startdrag", this.tree, data.node, e);
13938     },
13939     
13940     getRepairXY : function(e, data){
13941         return data.node.ui.getDDRepairXY();
13942     },
13943     
13944     onEndDrag : function(data, e){
13945         this.tree.fireEvent("enddrag", this.tree, data.node, e);
13946         
13947         
13948     },
13949     
13950     onValidDrop : function(dd, e, id){
13951         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
13952         this.hideProxy();
13953     },
13954     
13955     beforeInvalidDrop : function(e, id){
13956         // this scrolls the original position back into view
13957         var sm = this.tree.getSelectionModel();
13958         sm.clearSelections();
13959         sm.select(this.dragData.node);
13960     }
13961 });
13962 }/*
13963  * Based on:
13964  * Ext JS Library 1.1.1
13965  * Copyright(c) 2006-2007, Ext JS, LLC.
13966  *
13967  * Originally Released Under LGPL - original licence link has changed is not relivant.
13968  *
13969  * Fork - LGPL
13970  * <script type="text/javascript">
13971  */
13972 /**
13973  * @class Roo.tree.TreeEditor
13974  * @extends Roo.Editor
13975  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
13976  * as the editor field.
13977  * @constructor
13978  * @param {Object} config (used to be the tree panel.)
13979  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
13980  * 
13981  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
13982  * @cfg {Roo.form.TextField} field [required] The field configuration
13983  *
13984  * 
13985  */
13986 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
13987     var tree = config;
13988     var field;
13989     if (oldconfig) { // old style..
13990         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
13991     } else {
13992         // new style..
13993         tree = config.tree;
13994         config.field = config.field  || {};
13995         config.field.xtype = 'TextField';
13996         field = Roo.factory(config.field, Roo.form);
13997     }
13998     config = config || {};
13999     
14000     
14001     this.addEvents({
14002         /**
14003          * @event beforenodeedit
14004          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14005          * false from the handler of this event.
14006          * @param {Editor} this
14007          * @param {Roo.tree.Node} node 
14008          */
14009         "beforenodeedit" : true
14010     });
14011     
14012     //Roo.log(config);
14013     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14014
14015     this.tree = tree;
14016
14017     tree.on('beforeclick', this.beforeNodeClick, this);
14018     tree.getTreeEl().on('mousedown', this.hide, this);
14019     this.on('complete', this.updateNode, this);
14020     this.on('beforestartedit', this.fitToTree, this);
14021     this.on('startedit', this.bindScroll, this, {delay:10});
14022     this.on('specialkey', this.onSpecialKey, this);
14023 };
14024
14025 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14026     /**
14027      * @cfg {String} alignment
14028      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14029      */
14030     alignment: "l-l",
14031     // inherit
14032     autoSize: false,
14033     /**
14034      * @cfg {Boolean} hideEl
14035      * True to hide the bound element while the editor is displayed (defaults to false)
14036      */
14037     hideEl : false,
14038     /**
14039      * @cfg {String} cls
14040      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14041      */
14042     cls: "x-small-editor x-tree-editor",
14043     /**
14044      * @cfg {Boolean} shim
14045      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14046      */
14047     shim:false,
14048     // inherit
14049     shadow:"frame",
14050     /**
14051      * @cfg {Number} maxWidth
14052      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14053      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14054      * scroll and client offsets into account prior to each edit.
14055      */
14056     maxWidth: 250,
14057
14058     editDelay : 350,
14059
14060     // private
14061     fitToTree : function(ed, el){
14062         var td = this.tree.getTreeEl().dom, nd = el.dom;
14063         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14064             td.scrollLeft = nd.offsetLeft;
14065         }
14066         var w = Math.min(
14067                 this.maxWidth,
14068                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14069         this.setSize(w, '');
14070         
14071         return this.fireEvent('beforenodeedit', this, this.editNode);
14072         
14073     },
14074
14075     // private
14076     triggerEdit : function(node){
14077         this.completeEdit();
14078         this.editNode = node;
14079         this.startEdit(node.ui.textNode, node.text);
14080     },
14081
14082     // private
14083     bindScroll : function(){
14084         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14085     },
14086
14087     // private
14088     beforeNodeClick : function(node, e){
14089         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14090         this.lastClick = new Date();
14091         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14092             e.stopEvent();
14093             this.triggerEdit(node);
14094             return false;
14095         }
14096         return true;
14097     },
14098
14099     // private
14100     updateNode : function(ed, value){
14101         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14102         this.editNode.setText(value);
14103     },
14104
14105     // private
14106     onHide : function(){
14107         Roo.tree.TreeEditor.superclass.onHide.call(this);
14108         if(this.editNode){
14109             this.editNode.ui.focus();
14110         }
14111     },
14112
14113     // private
14114     onSpecialKey : function(field, e){
14115         var k = e.getKey();
14116         if(k == e.ESC){
14117             e.stopEvent();
14118             this.cancelEdit();
14119         }else if(k == e.ENTER && !e.hasModifier()){
14120             e.stopEvent();
14121             this.completeEdit();
14122         }
14123     }
14124 });//<Script type="text/javascript">
14125 /*
14126  * Based on:
14127  * Ext JS Library 1.1.1
14128  * Copyright(c) 2006-2007, Ext JS, LLC.
14129  *
14130  * Originally Released Under LGPL - original licence link has changed is not relivant.
14131  *
14132  * Fork - LGPL
14133  * <script type="text/javascript">
14134  */
14135  
14136 /**
14137  * Not documented??? - probably should be...
14138  */
14139
14140 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14141     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14142     
14143     renderElements : function(n, a, targetNode, bulkRender){
14144         //consel.log("renderElements?");
14145         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14146
14147         var t = n.getOwnerTree();
14148         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14149         
14150         var cols = t.columns;
14151         var bw = t.borderWidth;
14152         var c = cols[0];
14153         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14154          var cb = typeof a.checked == "boolean";
14155         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14156         var colcls = 'x-t-' + tid + '-c0';
14157         var buf = [
14158             '<li class="x-tree-node">',
14159             
14160                 
14161                 '<div class="x-tree-node-el ', a.cls,'">',
14162                     // extran...
14163                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14164                 
14165                 
14166                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14167                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14168                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14169                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14170                            (a.iconCls ? ' '+a.iconCls : ''),
14171                            '" unselectable="on" />',
14172                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14173                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14174                              
14175                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14176                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14177                             '<span unselectable="on" qtip="' + tx + '">',
14178                              tx,
14179                              '</span></a>' ,
14180                     '</div>',
14181                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14182                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14183                  ];
14184         for(var i = 1, len = cols.length; i < len; i++){
14185             c = cols[i];
14186             colcls = 'x-t-' + tid + '-c' +i;
14187             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14188             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14189                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14190                       "</div>");
14191          }
14192          
14193          buf.push(
14194             '</a>',
14195             '<div class="x-clear"></div></div>',
14196             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14197             "</li>");
14198         
14199         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14200             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14201                                 n.nextSibling.ui.getEl(), buf.join(""));
14202         }else{
14203             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14204         }
14205         var el = this.wrap.firstChild;
14206         this.elRow = el;
14207         this.elNode = el.firstChild;
14208         this.ranchor = el.childNodes[1];
14209         this.ctNode = this.wrap.childNodes[1];
14210         var cs = el.firstChild.childNodes;
14211         this.indentNode = cs[0];
14212         this.ecNode = cs[1];
14213         this.iconNode = cs[2];
14214         var index = 3;
14215         if(cb){
14216             this.checkbox = cs[3];
14217             index++;
14218         }
14219         this.anchor = cs[index];
14220         
14221         this.textNode = cs[index].firstChild;
14222         
14223         //el.on("click", this.onClick, this);
14224         //el.on("dblclick", this.onDblClick, this);
14225         
14226         
14227        // console.log(this);
14228     },
14229     initEvents : function(){
14230         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14231         
14232             
14233         var a = this.ranchor;
14234
14235         var el = Roo.get(a);
14236
14237         if(Roo.isOpera){ // opera render bug ignores the CSS
14238             el.setStyle("text-decoration", "none");
14239         }
14240
14241         el.on("click", this.onClick, this);
14242         el.on("dblclick", this.onDblClick, this);
14243         el.on("contextmenu", this.onContextMenu, this);
14244         
14245     },
14246     
14247     /*onSelectedChange : function(state){
14248         if(state){
14249             this.focus();
14250             this.addClass("x-tree-selected");
14251         }else{
14252             //this.blur();
14253             this.removeClass("x-tree-selected");
14254         }
14255     },*/
14256     addClass : function(cls){
14257         if(this.elRow){
14258             Roo.fly(this.elRow).addClass(cls);
14259         }
14260         
14261     },
14262     
14263     
14264     removeClass : function(cls){
14265         if(this.elRow){
14266             Roo.fly(this.elRow).removeClass(cls);
14267         }
14268     }
14269
14270     
14271     
14272 });//<Script type="text/javascript">
14273
14274 /*
14275  * Based on:
14276  * Ext JS Library 1.1.1
14277  * Copyright(c) 2006-2007, Ext JS, LLC.
14278  *
14279  * Originally Released Under LGPL - original licence link has changed is not relivant.
14280  *
14281  * Fork - LGPL
14282  * <script type="text/javascript">
14283  */
14284  
14285
14286 /**
14287  * @class Roo.tree.ColumnTree
14288  * @extends Roo.data.TreePanel
14289  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14290  * @cfg {int} borderWidth  compined right/left border allowance
14291  * @constructor
14292  * @param {String/HTMLElement/Element} el The container element
14293  * @param {Object} config
14294  */
14295 Roo.tree.ColumnTree =  function(el, config)
14296 {
14297    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14298    this.addEvents({
14299         /**
14300         * @event resize
14301         * Fire this event on a container when it resizes
14302         * @param {int} w Width
14303         * @param {int} h Height
14304         */
14305        "resize" : true
14306     });
14307     this.on('resize', this.onResize, this);
14308 };
14309
14310 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14311     //lines:false,
14312     
14313     
14314     borderWidth: Roo.isBorderBox ? 0 : 2, 
14315     headEls : false,
14316     
14317     render : function(){
14318         // add the header.....
14319        
14320         Roo.tree.ColumnTree.superclass.render.apply(this);
14321         
14322         this.el.addClass('x-column-tree');
14323         
14324         this.headers = this.el.createChild(
14325             {cls:'x-tree-headers'},this.innerCt.dom);
14326    
14327         var cols = this.columns, c;
14328         var totalWidth = 0;
14329         this.headEls = [];
14330         var  len = cols.length;
14331         for(var i = 0; i < len; i++){
14332              c = cols[i];
14333              totalWidth += c.width;
14334             this.headEls.push(this.headers.createChild({
14335                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14336                  cn: {
14337                      cls:'x-tree-hd-text',
14338                      html: c.header
14339                  },
14340                  style:'width:'+(c.width-this.borderWidth)+'px;'
14341              }));
14342         }
14343         this.headers.createChild({cls:'x-clear'});
14344         // prevent floats from wrapping when clipped
14345         this.headers.setWidth(totalWidth);
14346         //this.innerCt.setWidth(totalWidth);
14347         this.innerCt.setStyle({ overflow: 'auto' });
14348         this.onResize(this.width, this.height);
14349              
14350         
14351     },
14352     onResize : function(w,h)
14353     {
14354         this.height = h;
14355         this.width = w;
14356         // resize cols..
14357         this.innerCt.setWidth(this.width);
14358         this.innerCt.setHeight(this.height-20);
14359         
14360         // headers...
14361         var cols = this.columns, c;
14362         var totalWidth = 0;
14363         var expEl = false;
14364         var len = cols.length;
14365         for(var i = 0; i < len; i++){
14366             c = cols[i];
14367             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14368                 // it's the expander..
14369                 expEl  = this.headEls[i];
14370                 continue;
14371             }
14372             totalWidth += c.width;
14373             
14374         }
14375         if (expEl) {
14376             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14377         }
14378         this.headers.setWidth(w-20);
14379
14380         
14381         
14382         
14383     }
14384 });
14385 /*
14386  * Based on:
14387  * Ext JS Library 1.1.1
14388  * Copyright(c) 2006-2007, Ext JS, LLC.
14389  *
14390  * Originally Released Under LGPL - original licence link has changed is not relivant.
14391  *
14392  * Fork - LGPL
14393  * <script type="text/javascript">
14394  */
14395  
14396 /**
14397  * @class Roo.menu.Menu
14398  * @extends Roo.util.Observable
14399  * @children Roo.menu.BaseItem
14400  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14401  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14402  * @constructor
14403  * Creates a new Menu
14404  * @param {Object} config Configuration options
14405  */
14406 Roo.menu.Menu = function(config){
14407     
14408     Roo.menu.Menu.superclass.constructor.call(this, config);
14409     
14410     this.id = this.id || Roo.id();
14411     this.addEvents({
14412         /**
14413          * @event beforeshow
14414          * Fires before this menu is displayed
14415          * @param {Roo.menu.Menu} this
14416          */
14417         beforeshow : true,
14418         /**
14419          * @event beforehide
14420          * Fires before this menu is hidden
14421          * @param {Roo.menu.Menu} this
14422          */
14423         beforehide : true,
14424         /**
14425          * @event show
14426          * Fires after this menu is displayed
14427          * @param {Roo.menu.Menu} this
14428          */
14429         show : true,
14430         /**
14431          * @event hide
14432          * Fires after this menu is hidden
14433          * @param {Roo.menu.Menu} this
14434          */
14435         hide : true,
14436         /**
14437          * @event click
14438          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14439          * @param {Roo.menu.Menu} this
14440          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14441          * @param {Roo.EventObject} e
14442          */
14443         click : true,
14444         /**
14445          * @event mouseover
14446          * Fires when the mouse is hovering over this menu
14447          * @param {Roo.menu.Menu} this
14448          * @param {Roo.EventObject} e
14449          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14450          */
14451         mouseover : true,
14452         /**
14453          * @event mouseout
14454          * Fires when the mouse exits this menu
14455          * @param {Roo.menu.Menu} this
14456          * @param {Roo.EventObject} e
14457          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14458          */
14459         mouseout : true,
14460         /**
14461          * @event itemclick
14462          * Fires when a menu item contained in this menu is clicked
14463          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14464          * @param {Roo.EventObject} e
14465          */
14466         itemclick: true
14467     });
14468     if (this.registerMenu) {
14469         Roo.menu.MenuMgr.register(this);
14470     }
14471     
14472     var mis = this.items;
14473     this.items = new Roo.util.MixedCollection();
14474     if(mis){
14475         this.add.apply(this, mis);
14476     }
14477 };
14478
14479 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14480     /**
14481      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14482      */
14483     minWidth : 120,
14484     /**
14485      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14486      * for bottom-right shadow (defaults to "sides")
14487      */
14488     shadow : "sides",
14489     /**
14490      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14491      * this menu (defaults to "tl-tr?")
14492      */
14493     subMenuAlign : "tl-tr?",
14494     /**
14495      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14496      * relative to its element of origin (defaults to "tl-bl?")
14497      */
14498     defaultAlign : "tl-bl?",
14499     /**
14500      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14501      */
14502     allowOtherMenus : false,
14503     /**
14504      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14505      */
14506     registerMenu : true,
14507
14508     hidden:true,
14509
14510     // private
14511     render : function(){
14512         if(this.el){
14513             return;
14514         }
14515         var el = this.el = new Roo.Layer({
14516             cls: "x-menu",
14517             shadow:this.shadow,
14518             constrain: false,
14519             parentEl: this.parentEl || document.body,
14520             zindex:15000
14521         });
14522
14523         this.keyNav = new Roo.menu.MenuNav(this);
14524
14525         if(this.plain){
14526             el.addClass("x-menu-plain");
14527         }
14528         if(this.cls){
14529             el.addClass(this.cls);
14530         }
14531         // generic focus element
14532         this.focusEl = el.createChild({
14533             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14534         });
14535         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14536         //disabling touch- as it's causing issues ..
14537         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14538         ul.on('click'   , this.onClick, this);
14539         
14540         
14541         ul.on("mouseover", this.onMouseOver, this);
14542         ul.on("mouseout", this.onMouseOut, this);
14543         this.items.each(function(item){
14544             if (item.hidden) {
14545                 return;
14546             }
14547             
14548             var li = document.createElement("li");
14549             li.className = "x-menu-list-item";
14550             ul.dom.appendChild(li);
14551             item.render(li, this);
14552         }, this);
14553         this.ul = ul;
14554         this.autoWidth();
14555     },
14556
14557     // private
14558     autoWidth : function(){
14559         var el = this.el, ul = this.ul;
14560         if(!el){
14561             return;
14562         }
14563         var w = this.width;
14564         if(w){
14565             el.setWidth(w);
14566         }else if(Roo.isIE){
14567             el.setWidth(this.minWidth);
14568             var t = el.dom.offsetWidth; // force recalc
14569             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14570         }
14571     },
14572
14573     // private
14574     delayAutoWidth : function(){
14575         if(this.rendered){
14576             if(!this.awTask){
14577                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14578             }
14579             this.awTask.delay(20);
14580         }
14581     },
14582
14583     // private
14584     findTargetItem : function(e){
14585         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14586         if(t && t.menuItemId){
14587             return this.items.get(t.menuItemId);
14588         }
14589     },
14590
14591     // private
14592     onClick : function(e){
14593         Roo.log("menu.onClick");
14594         var t = this.findTargetItem(e);
14595         if(!t){
14596             return;
14597         }
14598         Roo.log(e);
14599         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14600             if(t == this.activeItem && t.shouldDeactivate(e)){
14601                 this.activeItem.deactivate();
14602                 delete this.activeItem;
14603                 return;
14604             }
14605             if(t.canActivate){
14606                 this.setActiveItem(t, true);
14607             }
14608             return;
14609             
14610             
14611         }
14612         
14613         t.onClick(e);
14614         this.fireEvent("click", this, t, e);
14615     },
14616
14617     // private
14618     setActiveItem : function(item, autoExpand){
14619         if(item != this.activeItem){
14620             if(this.activeItem){
14621                 this.activeItem.deactivate();
14622             }
14623             this.activeItem = item;
14624             item.activate(autoExpand);
14625         }else if(autoExpand){
14626             item.expandMenu();
14627         }
14628     },
14629
14630     // private
14631     tryActivate : function(start, step){
14632         var items = this.items;
14633         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
14634             var item = items.get(i);
14635             if(!item.disabled && item.canActivate){
14636                 this.setActiveItem(item, false);
14637                 return item;
14638             }
14639         }
14640         return false;
14641     },
14642
14643     // private
14644     onMouseOver : function(e){
14645         var t;
14646         if(t = this.findTargetItem(e)){
14647             if(t.canActivate && !t.disabled){
14648                 this.setActiveItem(t, true);
14649             }
14650         }
14651         this.fireEvent("mouseover", this, e, t);
14652     },
14653
14654     // private
14655     onMouseOut : function(e){
14656         var t;
14657         if(t = this.findTargetItem(e)){
14658             if(t == this.activeItem && t.shouldDeactivate(e)){
14659                 this.activeItem.deactivate();
14660                 delete this.activeItem;
14661             }
14662         }
14663         this.fireEvent("mouseout", this, e, t);
14664     },
14665
14666     /**
14667      * Read-only.  Returns true if the menu is currently displayed, else false.
14668      * @type Boolean
14669      */
14670     isVisible : function(){
14671         return this.el && !this.hidden;
14672     },
14673
14674     /**
14675      * Displays this menu relative to another element
14676      * @param {String/HTMLElement/Roo.Element} element The element to align to
14677      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
14678      * the element (defaults to this.defaultAlign)
14679      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14680      */
14681     show : function(el, pos, parentMenu){
14682         this.parentMenu = parentMenu;
14683         if(!this.el){
14684             this.render();
14685         }
14686         this.fireEvent("beforeshow", this);
14687         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
14688     },
14689
14690     /**
14691      * Displays this menu at a specific xy position
14692      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
14693      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14694      */
14695     showAt : function(xy, parentMenu, /* private: */_e){
14696         this.parentMenu = parentMenu;
14697         if(!this.el){
14698             this.render();
14699         }
14700         if(_e !== false){
14701             this.fireEvent("beforeshow", this);
14702             xy = this.el.adjustForConstraints(xy);
14703         }
14704         this.el.setXY(xy);
14705         this.el.show();
14706         this.hidden = false;
14707         this.focus();
14708         this.fireEvent("show", this);
14709     },
14710
14711     focus : function(){
14712         if(!this.hidden){
14713             this.doFocus.defer(50, this);
14714         }
14715     },
14716
14717     doFocus : function(){
14718         if(!this.hidden){
14719             this.focusEl.focus();
14720         }
14721     },
14722
14723     /**
14724      * Hides this menu and optionally all parent menus
14725      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
14726      */
14727     hide : function(deep){
14728         if(this.el && this.isVisible()){
14729             this.fireEvent("beforehide", this);
14730             if(this.activeItem){
14731                 this.activeItem.deactivate();
14732                 this.activeItem = null;
14733             }
14734             this.el.hide();
14735             this.hidden = true;
14736             this.fireEvent("hide", this);
14737         }
14738         if(deep === true && this.parentMenu){
14739             this.parentMenu.hide(true);
14740         }
14741     },
14742
14743     /**
14744      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
14745      * Any of the following are valid:
14746      * <ul>
14747      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
14748      * <li>An HTMLElement object which will be converted to a menu item</li>
14749      * <li>A menu item config object that will be created as a new menu item</li>
14750      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
14751      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
14752      * </ul>
14753      * Usage:
14754      * <pre><code>
14755 // Create the menu
14756 var menu = new Roo.menu.Menu();
14757
14758 // Create a menu item to add by reference
14759 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
14760
14761 // Add a bunch of items at once using different methods.
14762 // Only the last item added will be returned.
14763 var item = menu.add(
14764     menuItem,                // add existing item by ref
14765     'Dynamic Item',          // new TextItem
14766     '-',                     // new separator
14767     { text: 'Config Item' }  // new item by config
14768 );
14769 </code></pre>
14770      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
14771      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
14772      */
14773     add : function(){
14774         var a = arguments, l = a.length, item;
14775         for(var i = 0; i < l; i++){
14776             var el = a[i];
14777             if ((typeof(el) == "object") && el.xtype && el.xns) {
14778                 el = Roo.factory(el, Roo.menu);
14779             }
14780             
14781             if(el.render){ // some kind of Item
14782                 item = this.addItem(el);
14783             }else if(typeof el == "string"){ // string
14784                 if(el == "separator" || el == "-"){
14785                     item = this.addSeparator();
14786                 }else{
14787                     item = this.addText(el);
14788                 }
14789             }else if(el.tagName || el.el){ // element
14790                 item = this.addElement(el);
14791             }else if(typeof el == "object"){ // must be menu item config?
14792                 item = this.addMenuItem(el);
14793             }
14794         }
14795         return item;
14796     },
14797
14798     /**
14799      * Returns this menu's underlying {@link Roo.Element} object
14800      * @return {Roo.Element} The element
14801      */
14802     getEl : function(){
14803         if(!this.el){
14804             this.render();
14805         }
14806         return this.el;
14807     },
14808
14809     /**
14810      * Adds a separator bar to the menu
14811      * @return {Roo.menu.Item} The menu item that was added
14812      */
14813     addSeparator : function(){
14814         return this.addItem(new Roo.menu.Separator());
14815     },
14816
14817     /**
14818      * Adds an {@link Roo.Element} object to the menu
14819      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
14820      * @return {Roo.menu.Item} The menu item that was added
14821      */
14822     addElement : function(el){
14823         return this.addItem(new Roo.menu.BaseItem(el));
14824     },
14825
14826     /**
14827      * Adds an existing object based on {@link Roo.menu.Item} to the menu
14828      * @param {Roo.menu.Item} item The menu item to add
14829      * @return {Roo.menu.Item} The menu item that was added
14830      */
14831     addItem : function(item){
14832         this.items.add(item);
14833         if(this.ul){
14834             var li = document.createElement("li");
14835             li.className = "x-menu-list-item";
14836             this.ul.dom.appendChild(li);
14837             item.render(li, this);
14838             this.delayAutoWidth();
14839         }
14840         return item;
14841     },
14842
14843     /**
14844      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
14845      * @param {Object} config A MenuItem config object
14846      * @return {Roo.menu.Item} The menu item that was added
14847      */
14848     addMenuItem : function(config){
14849         if(!(config instanceof Roo.menu.Item)){
14850             if(typeof config.checked == "boolean"){ // must be check menu item config?
14851                 config = new Roo.menu.CheckItem(config);
14852             }else{
14853                 config = new Roo.menu.Item(config);
14854             }
14855         }
14856         return this.addItem(config);
14857     },
14858
14859     /**
14860      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
14861      * @param {String} text The text to display in the menu item
14862      * @return {Roo.menu.Item} The menu item that was added
14863      */
14864     addText : function(text){
14865         return this.addItem(new Roo.menu.TextItem({ text : text }));
14866     },
14867
14868     /**
14869      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
14870      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
14871      * @param {Roo.menu.Item} item The menu item to add
14872      * @return {Roo.menu.Item} The menu item that was added
14873      */
14874     insert : function(index, item){
14875         this.items.insert(index, item);
14876         if(this.ul){
14877             var li = document.createElement("li");
14878             li.className = "x-menu-list-item";
14879             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
14880             item.render(li, this);
14881             this.delayAutoWidth();
14882         }
14883         return item;
14884     },
14885
14886     /**
14887      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
14888      * @param {Roo.menu.Item} item The menu item to remove
14889      */
14890     remove : function(item){
14891         this.items.removeKey(item.id);
14892         item.destroy();
14893     },
14894
14895     /**
14896      * Removes and destroys all items in the menu
14897      */
14898     removeAll : function(){
14899         var f;
14900         while(f = this.items.first()){
14901             this.remove(f);
14902         }
14903     }
14904 });
14905
14906 // MenuNav is a private utility class used internally by the Menu
14907 Roo.menu.MenuNav = function(menu){
14908     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
14909     this.scope = this.menu = menu;
14910 };
14911
14912 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
14913     doRelay : function(e, h){
14914         var k = e.getKey();
14915         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
14916             this.menu.tryActivate(0, 1);
14917             return false;
14918         }
14919         return h.call(this.scope || this, e, this.menu);
14920     },
14921
14922     up : function(e, m){
14923         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
14924             m.tryActivate(m.items.length-1, -1);
14925         }
14926     },
14927
14928     down : function(e, m){
14929         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
14930             m.tryActivate(0, 1);
14931         }
14932     },
14933
14934     right : function(e, m){
14935         if(m.activeItem){
14936             m.activeItem.expandMenu(true);
14937         }
14938     },
14939
14940     left : function(e, m){
14941         m.hide();
14942         if(m.parentMenu && m.parentMenu.activeItem){
14943             m.parentMenu.activeItem.activate();
14944         }
14945     },
14946
14947     enter : function(e, m){
14948         if(m.activeItem){
14949             e.stopPropagation();
14950             m.activeItem.onClick(e);
14951             m.fireEvent("click", this, m.activeItem);
14952             return true;
14953         }
14954     }
14955 });/*
14956  * Based on:
14957  * Ext JS Library 1.1.1
14958  * Copyright(c) 2006-2007, Ext JS, LLC.
14959  *
14960  * Originally Released Under LGPL - original licence link has changed is not relivant.
14961  *
14962  * Fork - LGPL
14963  * <script type="text/javascript">
14964  */
14965  
14966 /**
14967  * @class Roo.menu.MenuMgr
14968  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
14969  * @singleton
14970  */
14971 Roo.menu.MenuMgr = function(){
14972    var menus, active, groups = {}, attached = false, lastShow = new Date();
14973
14974    // private - called when first menu is created
14975    function init(){
14976        menus = {};
14977        active = new Roo.util.MixedCollection();
14978        Roo.get(document).addKeyListener(27, function(){
14979            if(active.length > 0){
14980                hideAll();
14981            }
14982        });
14983    }
14984
14985    // private
14986    function hideAll(){
14987        if(active && active.length > 0){
14988            var c = active.clone();
14989            c.each(function(m){
14990                m.hide();
14991            });
14992        }
14993    }
14994
14995    // private
14996    function onHide(m){
14997        active.remove(m);
14998        if(active.length < 1){
14999            Roo.get(document).un("mousedown", onMouseDown);
15000            attached = false;
15001        }
15002    }
15003
15004    // private
15005    function onShow(m){
15006        var last = active.last();
15007        lastShow = new Date();
15008        active.add(m);
15009        if(!attached){
15010            Roo.get(document).on("mousedown", onMouseDown);
15011            attached = true;
15012        }
15013        if(m.parentMenu){
15014           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15015           m.parentMenu.activeChild = m;
15016        }else if(last && last.isVisible()){
15017           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15018        }
15019    }
15020
15021    // private
15022    function onBeforeHide(m){
15023        if(m.activeChild){
15024            m.activeChild.hide();
15025        }
15026        if(m.autoHideTimer){
15027            clearTimeout(m.autoHideTimer);
15028            delete m.autoHideTimer;
15029        }
15030    }
15031
15032    // private
15033    function onBeforeShow(m){
15034        var pm = m.parentMenu;
15035        if(!pm && !m.allowOtherMenus){
15036            hideAll();
15037        }else if(pm && pm.activeChild && active != m){
15038            pm.activeChild.hide();
15039        }
15040    }
15041
15042    // private
15043    function onMouseDown(e){
15044        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15045            hideAll();
15046        }
15047    }
15048
15049    // private
15050    function onBeforeCheck(mi, state){
15051        if(state){
15052            var g = groups[mi.group];
15053            for(var i = 0, l = g.length; i < l; i++){
15054                if(g[i] != mi){
15055                    g[i].setChecked(false);
15056                }
15057            }
15058        }
15059    }
15060
15061    return {
15062
15063        /**
15064         * Hides all menus that are currently visible
15065         */
15066        hideAll : function(){
15067             hideAll();  
15068        },
15069
15070        // private
15071        register : function(menu){
15072            if(!menus){
15073                init();
15074            }
15075            menus[menu.id] = menu;
15076            menu.on("beforehide", onBeforeHide);
15077            menu.on("hide", onHide);
15078            menu.on("beforeshow", onBeforeShow);
15079            menu.on("show", onShow);
15080            var g = menu.group;
15081            if(g && menu.events["checkchange"]){
15082                if(!groups[g]){
15083                    groups[g] = [];
15084                }
15085                groups[g].push(menu);
15086                menu.on("checkchange", onCheck);
15087            }
15088        },
15089
15090         /**
15091          * Returns a {@link Roo.menu.Menu} object
15092          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15093          * be used to generate and return a new Menu instance.
15094          */
15095        get : function(menu){
15096            if(typeof menu == "string"){ // menu id
15097                return menus[menu];
15098            }else if(menu.events){  // menu instance
15099                return menu;
15100            }else if(typeof menu.length == 'number'){ // array of menu items?
15101                return new Roo.menu.Menu({items:menu});
15102            }else{ // otherwise, must be a config
15103                return new Roo.menu.Menu(menu);
15104            }
15105        },
15106
15107        // private
15108        unregister : function(menu){
15109            delete menus[menu.id];
15110            menu.un("beforehide", onBeforeHide);
15111            menu.un("hide", onHide);
15112            menu.un("beforeshow", onBeforeShow);
15113            menu.un("show", onShow);
15114            var g = menu.group;
15115            if(g && menu.events["checkchange"]){
15116                groups[g].remove(menu);
15117                menu.un("checkchange", onCheck);
15118            }
15119        },
15120
15121        // private
15122        registerCheckable : function(menuItem){
15123            var g = menuItem.group;
15124            if(g){
15125                if(!groups[g]){
15126                    groups[g] = [];
15127                }
15128                groups[g].push(menuItem);
15129                menuItem.on("beforecheckchange", onBeforeCheck);
15130            }
15131        },
15132
15133        // private
15134        unregisterCheckable : function(menuItem){
15135            var g = menuItem.group;
15136            if(g){
15137                groups[g].remove(menuItem);
15138                menuItem.un("beforecheckchange", onBeforeCheck);
15139            }
15140        }
15141    };
15142 }();/*
15143  * Based on:
15144  * Ext JS Library 1.1.1
15145  * Copyright(c) 2006-2007, Ext JS, LLC.
15146  *
15147  * Originally Released Under LGPL - original licence link has changed is not relivant.
15148  *
15149  * Fork - LGPL
15150  * <script type="text/javascript">
15151  */
15152  
15153
15154 /**
15155  * @class Roo.menu.BaseItem
15156  * @extends Roo.Component
15157  * @abstract
15158  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15159  * management and base configuration options shared by all menu components.
15160  * @constructor
15161  * Creates a new BaseItem
15162  * @param {Object} config Configuration options
15163  */
15164 Roo.menu.BaseItem = function(config){
15165     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15166
15167     this.addEvents({
15168         /**
15169          * @event click
15170          * Fires when this item is clicked
15171          * @param {Roo.menu.BaseItem} this
15172          * @param {Roo.EventObject} e
15173          */
15174         click: true,
15175         /**
15176          * @event activate
15177          * Fires when this item is activated
15178          * @param {Roo.menu.BaseItem} this
15179          */
15180         activate : true,
15181         /**
15182          * @event deactivate
15183          * Fires when this item is deactivated
15184          * @param {Roo.menu.BaseItem} this
15185          */
15186         deactivate : true
15187     });
15188
15189     if(this.handler){
15190         this.on("click", this.handler, this.scope, true);
15191     }
15192 };
15193
15194 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15195     /**
15196      * @cfg {Function} handler
15197      * A function that will handle the click event of this menu item (defaults to undefined)
15198      */
15199     /**
15200      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15201      */
15202     canActivate : false,
15203     
15204      /**
15205      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15206      */
15207     hidden: false,
15208     
15209     /**
15210      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15211      */
15212     activeClass : "x-menu-item-active",
15213     /**
15214      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15215      */
15216     hideOnClick : true,
15217     /**
15218      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15219      */
15220     hideDelay : 100,
15221
15222     // private
15223     ctype: "Roo.menu.BaseItem",
15224
15225     // private
15226     actionMode : "container",
15227
15228     // private
15229     render : function(container, parentMenu){
15230         this.parentMenu = parentMenu;
15231         Roo.menu.BaseItem.superclass.render.call(this, container);
15232         this.container.menuItemId = this.id;
15233     },
15234
15235     // private
15236     onRender : function(container, position){
15237         this.el = Roo.get(this.el);
15238         container.dom.appendChild(this.el.dom);
15239     },
15240
15241     // private
15242     onClick : function(e){
15243         if(!this.disabled && this.fireEvent("click", this, e) !== false
15244                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15245             this.handleClick(e);
15246         }else{
15247             e.stopEvent();
15248         }
15249     },
15250
15251     // private
15252     activate : function(){
15253         if(this.disabled){
15254             return false;
15255         }
15256         var li = this.container;
15257         li.addClass(this.activeClass);
15258         this.region = li.getRegion().adjust(2, 2, -2, -2);
15259         this.fireEvent("activate", this);
15260         return true;
15261     },
15262
15263     // private
15264     deactivate : function(){
15265         this.container.removeClass(this.activeClass);
15266         this.fireEvent("deactivate", this);
15267     },
15268
15269     // private
15270     shouldDeactivate : function(e){
15271         return !this.region || !this.region.contains(e.getPoint());
15272     },
15273
15274     // private
15275     handleClick : function(e){
15276         if(this.hideOnClick){
15277             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15278         }
15279     },
15280
15281     // private
15282     expandMenu : function(autoActivate){
15283         // do nothing
15284     },
15285
15286     // private
15287     hideMenu : function(){
15288         // do nothing
15289     }
15290 });/*
15291  * Based on:
15292  * Ext JS Library 1.1.1
15293  * Copyright(c) 2006-2007, Ext JS, LLC.
15294  *
15295  * Originally Released Under LGPL - original licence link has changed is not relivant.
15296  *
15297  * Fork - LGPL
15298  * <script type="text/javascript">
15299  */
15300  
15301 /**
15302  * @class Roo.menu.Adapter
15303  * @extends Roo.menu.BaseItem
15304  * @abstract
15305  * 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.
15306  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15307  * @constructor
15308  * Creates a new Adapter
15309  * @param {Object} config Configuration options
15310  */
15311 Roo.menu.Adapter = function(component, config){
15312     Roo.menu.Adapter.superclass.constructor.call(this, config);
15313     this.component = component;
15314 };
15315 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15316     // private
15317     canActivate : true,
15318
15319     // private
15320     onRender : function(container, position){
15321         this.component.render(container);
15322         this.el = this.component.getEl();
15323     },
15324
15325     // private
15326     activate : function(){
15327         if(this.disabled){
15328             return false;
15329         }
15330         this.component.focus();
15331         this.fireEvent("activate", this);
15332         return true;
15333     },
15334
15335     // private
15336     deactivate : function(){
15337         this.fireEvent("deactivate", this);
15338     },
15339
15340     // private
15341     disable : function(){
15342         this.component.disable();
15343         Roo.menu.Adapter.superclass.disable.call(this);
15344     },
15345
15346     // private
15347     enable : function(){
15348         this.component.enable();
15349         Roo.menu.Adapter.superclass.enable.call(this);
15350     }
15351 });/*
15352  * Based on:
15353  * Ext JS Library 1.1.1
15354  * Copyright(c) 2006-2007, Ext JS, LLC.
15355  *
15356  * Originally Released Under LGPL - original licence link has changed is not relivant.
15357  *
15358  * Fork - LGPL
15359  * <script type="text/javascript">
15360  */
15361
15362 /**
15363  * @class Roo.menu.TextItem
15364  * @extends Roo.menu.BaseItem
15365  * Adds a static text string to a menu, usually used as either a heading or group separator.
15366  * Note: old style constructor with text is still supported.
15367  * 
15368  * @constructor
15369  * Creates a new TextItem
15370  * @param {Object} cfg Configuration
15371  */
15372 Roo.menu.TextItem = function(cfg){
15373     if (typeof(cfg) == 'string') {
15374         this.text = cfg;
15375     } else {
15376         Roo.apply(this,cfg);
15377     }
15378     
15379     Roo.menu.TextItem.superclass.constructor.call(this);
15380 };
15381
15382 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15383     /**
15384      * @cfg {String} text Text to show on item.
15385      */
15386     text : '',
15387     
15388     /**
15389      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15390      */
15391     hideOnClick : false,
15392     /**
15393      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15394      */
15395     itemCls : "x-menu-text",
15396
15397     // private
15398     onRender : function(){
15399         var s = document.createElement("span");
15400         s.className = this.itemCls;
15401         s.innerHTML = this.text;
15402         this.el = s;
15403         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15404     }
15405 });/*
15406  * Based on:
15407  * Ext JS Library 1.1.1
15408  * Copyright(c) 2006-2007, Ext JS, LLC.
15409  *
15410  * Originally Released Under LGPL - original licence link has changed is not relivant.
15411  *
15412  * Fork - LGPL
15413  * <script type="text/javascript">
15414  */
15415
15416 /**
15417  * @class Roo.menu.Separator
15418  * @extends Roo.menu.BaseItem
15419  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15420  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15421  * @constructor
15422  * @param {Object} config Configuration options
15423  */
15424 Roo.menu.Separator = function(config){
15425     Roo.menu.Separator.superclass.constructor.call(this, config);
15426 };
15427
15428 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15429     /**
15430      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15431      */
15432     itemCls : "x-menu-sep",
15433     /**
15434      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15435      */
15436     hideOnClick : false,
15437
15438     // private
15439     onRender : function(li){
15440         var s = document.createElement("span");
15441         s.className = this.itemCls;
15442         s.innerHTML = "&#160;";
15443         this.el = s;
15444         li.addClass("x-menu-sep-li");
15445         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15446     }
15447 });/*
15448  * Based on:
15449  * Ext JS Library 1.1.1
15450  * Copyright(c) 2006-2007, Ext JS, LLC.
15451  *
15452  * Originally Released Under LGPL - original licence link has changed is not relivant.
15453  *
15454  * Fork - LGPL
15455  * <script type="text/javascript">
15456  */
15457 /**
15458  * @class Roo.menu.Item
15459  * @extends Roo.menu.BaseItem
15460  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15461  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15462  * activation and click handling.
15463  * @constructor
15464  * Creates a new Item
15465  * @param {Object} config Configuration options
15466  */
15467 Roo.menu.Item = function(config){
15468     Roo.menu.Item.superclass.constructor.call(this, config);
15469     if(this.menu){
15470         this.menu = Roo.menu.MenuMgr.get(this.menu);
15471     }
15472 };
15473 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15474     /**
15475      * @cfg {Roo.menu.Menu} menu
15476      * A Sub menu
15477      */
15478     /**
15479      * @cfg {String} text
15480      * The text to show on the menu item.
15481      */
15482     text: '',
15483      /**
15484      * @cfg {String} HTML to render in menu
15485      * The text to show on the menu item (HTML version).
15486      */
15487     html: '',
15488     /**
15489      * @cfg {String} icon
15490      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15491      */
15492     icon: undefined,
15493     /**
15494      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15495      */
15496     itemCls : "x-menu-item",
15497     /**
15498      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15499      */
15500     canActivate : true,
15501     /**
15502      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15503      */
15504     showDelay: 200,
15505     // doc'd in BaseItem
15506     hideDelay: 200,
15507
15508     // private
15509     ctype: "Roo.menu.Item",
15510     
15511     // private
15512     onRender : function(container, position){
15513         var el = document.createElement("a");
15514         el.hideFocus = true;
15515         el.unselectable = "on";
15516         el.href = this.href || "#";
15517         if(this.hrefTarget){
15518             el.target = this.hrefTarget;
15519         }
15520         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15521         
15522         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15523         
15524         el.innerHTML = String.format(
15525                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15526                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15527         this.el = el;
15528         Roo.menu.Item.superclass.onRender.call(this, container, position);
15529     },
15530
15531     /**
15532      * Sets the text to display in this menu item
15533      * @param {String} text The text to display
15534      * @param {Boolean} isHTML true to indicate text is pure html.
15535      */
15536     setText : function(text, isHTML){
15537         if (isHTML) {
15538             this.html = text;
15539         } else {
15540             this.text = text;
15541             this.html = '';
15542         }
15543         if(this.rendered){
15544             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15545      
15546             this.el.update(String.format(
15547                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15548                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15549             this.parentMenu.autoWidth();
15550         }
15551     },
15552
15553     // private
15554     handleClick : function(e){
15555         if(!this.href){ // if no link defined, stop the event automatically
15556             e.stopEvent();
15557         }
15558         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15559     },
15560
15561     // private
15562     activate : function(autoExpand){
15563         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15564             this.focus();
15565             if(autoExpand){
15566                 this.expandMenu();
15567             }
15568         }
15569         return true;
15570     },
15571
15572     // private
15573     shouldDeactivate : function(e){
15574         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15575             if(this.menu && this.menu.isVisible()){
15576                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15577             }
15578             return true;
15579         }
15580         return false;
15581     },
15582
15583     // private
15584     deactivate : function(){
15585         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15586         this.hideMenu();
15587     },
15588
15589     // private
15590     expandMenu : function(autoActivate){
15591         if(!this.disabled && this.menu){
15592             clearTimeout(this.hideTimer);
15593             delete this.hideTimer;
15594             if(!this.menu.isVisible() && !this.showTimer){
15595                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15596             }else if (this.menu.isVisible() && autoActivate){
15597                 this.menu.tryActivate(0, 1);
15598             }
15599         }
15600     },
15601
15602     // private
15603     deferExpand : function(autoActivate){
15604         delete this.showTimer;
15605         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15606         if(autoActivate){
15607             this.menu.tryActivate(0, 1);
15608         }
15609     },
15610
15611     // private
15612     hideMenu : function(){
15613         clearTimeout(this.showTimer);
15614         delete this.showTimer;
15615         if(!this.hideTimer && this.menu && this.menu.isVisible()){
15616             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15617         }
15618     },
15619
15620     // private
15621     deferHide : function(){
15622         delete this.hideTimer;
15623         this.menu.hide();
15624     }
15625 });/*
15626  * Based on:
15627  * Ext JS Library 1.1.1
15628  * Copyright(c) 2006-2007, Ext JS, LLC.
15629  *
15630  * Originally Released Under LGPL - original licence link has changed is not relivant.
15631  *
15632  * Fork - LGPL
15633  * <script type="text/javascript">
15634  */
15635  
15636 /**
15637  * @class Roo.menu.CheckItem
15638  * @extends Roo.menu.Item
15639  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
15640  * @constructor
15641  * Creates a new CheckItem
15642  * @param {Object} config Configuration options
15643  */
15644 Roo.menu.CheckItem = function(config){
15645     Roo.menu.CheckItem.superclass.constructor.call(this, config);
15646     this.addEvents({
15647         /**
15648          * @event beforecheckchange
15649          * Fires before the checked value is set, providing an opportunity to cancel if needed
15650          * @param {Roo.menu.CheckItem} this
15651          * @param {Boolean} checked The new checked value that will be set
15652          */
15653         "beforecheckchange" : true,
15654         /**
15655          * @event checkchange
15656          * Fires after the checked value has been set
15657          * @param {Roo.menu.CheckItem} this
15658          * @param {Boolean} checked The checked value that was set
15659          */
15660         "checkchange" : true
15661     });
15662     if(this.checkHandler){
15663         this.on('checkchange', this.checkHandler, this.scope);
15664     }
15665 };
15666 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
15667     /**
15668      * @cfg {String} group
15669      * All check items with the same group name will automatically be grouped into a single-select
15670      * radio button group (defaults to '')
15671      */
15672     /**
15673      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
15674      */
15675     itemCls : "x-menu-item x-menu-check-item",
15676     /**
15677      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
15678      */
15679     groupClass : "x-menu-group-item",
15680
15681     /**
15682      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
15683      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
15684      * initialized with checked = true will be rendered as checked.
15685      */
15686     checked: false,
15687
15688     // private
15689     ctype: "Roo.menu.CheckItem",
15690
15691     // private
15692     onRender : function(c){
15693         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
15694         if(this.group){
15695             this.el.addClass(this.groupClass);
15696         }
15697         Roo.menu.MenuMgr.registerCheckable(this);
15698         if(this.checked){
15699             this.checked = false;
15700             this.setChecked(true, true);
15701         }
15702     },
15703
15704     // private
15705     destroy : function(){
15706         if(this.rendered){
15707             Roo.menu.MenuMgr.unregisterCheckable(this);
15708         }
15709         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
15710     },
15711
15712     /**
15713      * Set the checked state of this item
15714      * @param {Boolean} checked The new checked value
15715      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
15716      */
15717     setChecked : function(state, suppressEvent){
15718         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
15719             if(this.container){
15720                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
15721             }
15722             this.checked = state;
15723             if(suppressEvent !== true){
15724                 this.fireEvent("checkchange", this, state);
15725             }
15726         }
15727     },
15728
15729     // private
15730     handleClick : function(e){
15731        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
15732            this.setChecked(!this.checked);
15733        }
15734        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
15735     }
15736 });/*
15737  * Based on:
15738  * Ext JS Library 1.1.1
15739  * Copyright(c) 2006-2007, Ext JS, LLC.
15740  *
15741  * Originally Released Under LGPL - original licence link has changed is not relivant.
15742  *
15743  * Fork - LGPL
15744  * <script type="text/javascript">
15745  */
15746  
15747 /**
15748  * @class Roo.menu.DateItem
15749  * @extends Roo.menu.Adapter
15750  * A menu item that wraps the {@link Roo.DatPicker} component.
15751  * @constructor
15752  * Creates a new DateItem
15753  * @param {Object} config Configuration options
15754  */
15755 Roo.menu.DateItem = function(config){
15756     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
15757     /** The Roo.DatePicker object @type Roo.DatePicker */
15758     this.picker = this.component;
15759     this.addEvents({select: true});
15760     
15761     this.picker.on("render", function(picker){
15762         picker.getEl().swallowEvent("click");
15763         picker.container.addClass("x-menu-date-item");
15764     });
15765
15766     this.picker.on("select", this.onSelect, this);
15767 };
15768
15769 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
15770     // private
15771     onSelect : function(picker, date){
15772         this.fireEvent("select", this, date, picker);
15773         Roo.menu.DateItem.superclass.handleClick.call(this);
15774     }
15775 });/*
15776  * Based on:
15777  * Ext JS Library 1.1.1
15778  * Copyright(c) 2006-2007, Ext JS, LLC.
15779  *
15780  * Originally Released Under LGPL - original licence link has changed is not relivant.
15781  *
15782  * Fork - LGPL
15783  * <script type="text/javascript">
15784  */
15785  
15786 /**
15787  * @class Roo.menu.ColorItem
15788  * @extends Roo.menu.Adapter
15789  * A menu item that wraps the {@link Roo.ColorPalette} component.
15790  * @constructor
15791  * Creates a new ColorItem
15792  * @param {Object} config Configuration options
15793  */
15794 Roo.menu.ColorItem = function(config){
15795     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
15796     /** The Roo.ColorPalette object @type Roo.ColorPalette */
15797     this.palette = this.component;
15798     this.relayEvents(this.palette, ["select"]);
15799     if(this.selectHandler){
15800         this.on('select', this.selectHandler, this.scope);
15801     }
15802 };
15803 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
15804  * Based on:
15805  * Ext JS Library 1.1.1
15806  * Copyright(c) 2006-2007, Ext JS, LLC.
15807  *
15808  * Originally Released Under LGPL - original licence link has changed is not relivant.
15809  *
15810  * Fork - LGPL
15811  * <script type="text/javascript">
15812  */
15813  
15814
15815 /**
15816  * @class Roo.menu.DateMenu
15817  * @extends Roo.menu.Menu
15818  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
15819  * @constructor
15820  * Creates a new DateMenu
15821  * @param {Object} config Configuration options
15822  */
15823 Roo.menu.DateMenu = function(config){
15824     Roo.menu.DateMenu.superclass.constructor.call(this, config);
15825     this.plain = true;
15826     var di = new Roo.menu.DateItem(config);
15827     this.add(di);
15828     /**
15829      * The {@link Roo.DatePicker} instance for this DateMenu
15830      * @type DatePicker
15831      */
15832     this.picker = di.picker;
15833     /**
15834      * @event select
15835      * @param {DatePicker} picker
15836      * @param {Date} date
15837      */
15838     this.relayEvents(di, ["select"]);
15839     this.on('beforeshow', function(){
15840         if(this.picker){
15841             this.picker.hideMonthPicker(false);
15842         }
15843     }, this);
15844 };
15845 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
15846     cls:'x-date-menu'
15847 });/*
15848  * Based on:
15849  * Ext JS Library 1.1.1
15850  * Copyright(c) 2006-2007, Ext JS, LLC.
15851  *
15852  * Originally Released Under LGPL - original licence link has changed is not relivant.
15853  *
15854  * Fork - LGPL
15855  * <script type="text/javascript">
15856  */
15857  
15858
15859 /**
15860  * @class Roo.menu.ColorMenu
15861  * @extends Roo.menu.Menu
15862  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
15863  * @constructor
15864  * Creates a new ColorMenu
15865  * @param {Object} config Configuration options
15866  */
15867 Roo.menu.ColorMenu = function(config){
15868     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
15869     this.plain = true;
15870     var ci = new Roo.menu.ColorItem(config);
15871     this.add(ci);
15872     /**
15873      * The {@link Roo.ColorPalette} instance for this ColorMenu
15874      * @type ColorPalette
15875      */
15876     this.palette = ci.palette;
15877     /**
15878      * @event select
15879      * @param {ColorPalette} palette
15880      * @param {String} color
15881      */
15882     this.relayEvents(ci, ["select"]);
15883 };
15884 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
15885  * Based on:
15886  * Ext JS Library 1.1.1
15887  * Copyright(c) 2006-2007, Ext JS, LLC.
15888  *
15889  * Originally Released Under LGPL - original licence link has changed is not relivant.
15890  *
15891  * Fork - LGPL
15892  * <script type="text/javascript">
15893  */
15894  
15895 /**
15896  * @class Roo.form.TextItem
15897  * @extends Roo.BoxComponent
15898  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15899  * @constructor
15900  * Creates a new TextItem
15901  * @param {Object} config Configuration options
15902  */
15903 Roo.form.TextItem = function(config){
15904     Roo.form.TextItem.superclass.constructor.call(this, config);
15905 };
15906
15907 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
15908     
15909     /**
15910      * @cfg {String} tag the tag for this item (default div)
15911      */
15912     tag : 'div',
15913     /**
15914      * @cfg {String} html the content for this item
15915      */
15916     html : '',
15917     
15918     getAutoCreate : function()
15919     {
15920         var cfg = {
15921             id: this.id,
15922             tag: this.tag,
15923             html: this.html,
15924             cls: 'x-form-item'
15925         };
15926         
15927         return cfg;
15928         
15929     },
15930     
15931     onRender : function(ct, position)
15932     {
15933         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
15934         
15935         if(!this.el){
15936             var cfg = this.getAutoCreate();
15937             if(!cfg.name){
15938                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
15939             }
15940             if (!cfg.name.length) {
15941                 delete cfg.name;
15942             }
15943             this.el = ct.createChild(cfg, position);
15944         }
15945     },
15946     /*
15947      * setHTML
15948      * @param {String} html update the Contents of the element.
15949      */
15950     setHTML : function(html)
15951     {
15952         this.fieldEl.dom.innerHTML = html;
15953     }
15954     
15955 });/*
15956  * Based on:
15957  * Ext JS Library 1.1.1
15958  * Copyright(c) 2006-2007, Ext JS, LLC.
15959  *
15960  * Originally Released Under LGPL - original licence link has changed is not relivant.
15961  *
15962  * Fork - LGPL
15963  * <script type="text/javascript">
15964  */
15965  
15966 /**
15967  * @class Roo.form.Field
15968  * @extends Roo.BoxComponent
15969  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15970  * @constructor
15971  * Creates a new Field
15972  * @param {Object} config Configuration options
15973  */
15974 Roo.form.Field = function(config){
15975     Roo.form.Field.superclass.constructor.call(this, config);
15976 };
15977
15978 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
15979     /**
15980      * @cfg {String} fieldLabel Label to use when rendering a form.
15981      */
15982        /**
15983      * @cfg {String} qtip Mouse over tip
15984      */
15985      
15986     /**
15987      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
15988      */
15989     invalidClass : "x-form-invalid",
15990     /**
15991      * @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")
15992      */
15993     invalidText : "The value in this field is invalid",
15994     /**
15995      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
15996      */
15997     focusClass : "x-form-focus",
15998     /**
15999      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16000       automatic validation (defaults to "keyup").
16001      */
16002     validationEvent : "keyup",
16003     /**
16004      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16005      */
16006     validateOnBlur : true,
16007     /**
16008      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16009      */
16010     validationDelay : 250,
16011     /**
16012      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16013      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16014      */
16015     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16016     /**
16017      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16018      */
16019     fieldClass : "x-form-field",
16020     /**
16021      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16022      *<pre>
16023 Value         Description
16024 -----------   ----------------------------------------------------------------------
16025 qtip          Display a quick tip when the user hovers over the field
16026 title         Display a default browser title attribute popup
16027 under         Add a block div beneath the field containing the error text
16028 side          Add an error icon to the right of the field with a popup on hover
16029 [element id]  Add the error text directly to the innerHTML of the specified element
16030 </pre>
16031      */
16032     msgTarget : 'qtip',
16033     /**
16034      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16035      */
16036     msgFx : 'normal',
16037
16038     /**
16039      * @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.
16040      */
16041     readOnly : false,
16042
16043     /**
16044      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16045      */
16046     disabled : false,
16047
16048     /**
16049      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16050      */
16051     inputType : undefined,
16052     
16053     /**
16054      * @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).
16055          */
16056         tabIndex : undefined,
16057         
16058     // private
16059     isFormField : true,
16060
16061     // private
16062     hasFocus : false,
16063     /**
16064      * @property {Roo.Element} fieldEl
16065      * Element Containing the rendered Field (with label etc.)
16066      */
16067     /**
16068      * @cfg {Mixed} value A value to initialize this field with.
16069      */
16070     value : undefined,
16071
16072     /**
16073      * @cfg {String} name The field's HTML name attribute.
16074      */
16075     /**
16076      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16077      */
16078     // private
16079     loadedValue : false,
16080      
16081      
16082         // private ??
16083         initComponent : function(){
16084         Roo.form.Field.superclass.initComponent.call(this);
16085         this.addEvents({
16086             /**
16087              * @event focus
16088              * Fires when this field receives input focus.
16089              * @param {Roo.form.Field} this
16090              */
16091             focus : true,
16092             /**
16093              * @event blur
16094              * Fires when this field loses input focus.
16095              * @param {Roo.form.Field} this
16096              */
16097             blur : true,
16098             /**
16099              * @event specialkey
16100              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16101              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16102              * @param {Roo.form.Field} this
16103              * @param {Roo.EventObject} e The event object
16104              */
16105             specialkey : true,
16106             /**
16107              * @event change
16108              * Fires just before the field blurs if the field value has changed.
16109              * @param {Roo.form.Field} this
16110              * @param {Mixed} newValue The new value
16111              * @param {Mixed} oldValue The original value
16112              */
16113             change : true,
16114             /**
16115              * @event invalid
16116              * Fires after the field has been marked as invalid.
16117              * @param {Roo.form.Field} this
16118              * @param {String} msg The validation message
16119              */
16120             invalid : true,
16121             /**
16122              * @event valid
16123              * Fires after the field has been validated with no errors.
16124              * @param {Roo.form.Field} this
16125              */
16126             valid : true,
16127              /**
16128              * @event keyup
16129              * Fires after the key up
16130              * @param {Roo.form.Field} this
16131              * @param {Roo.EventObject}  e The event Object
16132              */
16133             keyup : true
16134         });
16135     },
16136
16137     /**
16138      * Returns the name attribute of the field if available
16139      * @return {String} name The field name
16140      */
16141     getName: function(){
16142          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16143     },
16144
16145     // private
16146     onRender : function(ct, position){
16147         Roo.form.Field.superclass.onRender.call(this, ct, position);
16148         if(!this.el){
16149             var cfg = this.getAutoCreate();
16150             if(!cfg.name){
16151                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16152             }
16153             if (!cfg.name.length) {
16154                 delete cfg.name;
16155             }
16156             if(this.inputType){
16157                 cfg.type = this.inputType;
16158             }
16159             this.el = ct.createChild(cfg, position);
16160         }
16161         var type = this.el.dom.type;
16162         if(type){
16163             if(type == 'password'){
16164                 type = 'text';
16165             }
16166             this.el.addClass('x-form-'+type);
16167         }
16168         if(this.readOnly){
16169             this.el.dom.readOnly = true;
16170         }
16171         if(this.tabIndex !== undefined){
16172             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16173         }
16174
16175         this.el.addClass([this.fieldClass, this.cls]);
16176         this.initValue();
16177     },
16178
16179     /**
16180      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16181      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16182      * @return {Roo.form.Field} this
16183      */
16184     applyTo : function(target){
16185         this.allowDomMove = false;
16186         this.el = Roo.get(target);
16187         this.render(this.el.dom.parentNode);
16188         return this;
16189     },
16190
16191     // private
16192     initValue : function(){
16193         if(this.value !== undefined){
16194             this.setValue(this.value);
16195         }else if(this.el.dom.value.length > 0){
16196             this.setValue(this.el.dom.value);
16197         }
16198     },
16199
16200     /**
16201      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16202      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16203      */
16204     isDirty : function() {
16205         if(this.disabled) {
16206             return false;
16207         }
16208         return String(this.getValue()) !== String(this.originalValue);
16209     },
16210
16211     /**
16212      * stores the current value in loadedValue
16213      */
16214     resetHasChanged : function()
16215     {
16216         this.loadedValue = String(this.getValue());
16217     },
16218     /**
16219      * checks the current value against the 'loaded' value.
16220      * Note - will return false if 'resetHasChanged' has not been called first.
16221      */
16222     hasChanged : function()
16223     {
16224         if(this.disabled || this.readOnly) {
16225             return false;
16226         }
16227         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16228     },
16229     
16230     
16231     
16232     // private
16233     afterRender : function(){
16234         Roo.form.Field.superclass.afterRender.call(this);
16235         this.initEvents();
16236     },
16237
16238     // private
16239     fireKey : function(e){
16240         //Roo.log('field ' + e.getKey());
16241         if(e.isNavKeyPress()){
16242             this.fireEvent("specialkey", this, e);
16243         }
16244     },
16245
16246     /**
16247      * Resets the current field value to the originally loaded value and clears any validation messages
16248      */
16249     reset : function(){
16250         this.setValue(this.resetValue);
16251         this.originalValue = this.getValue();
16252         this.clearInvalid();
16253     },
16254
16255     // private
16256     initEvents : function(){
16257         // safari killled keypress - so keydown is now used..
16258         this.el.on("keydown" , this.fireKey,  this);
16259         this.el.on("focus", this.onFocus,  this);
16260         this.el.on("blur", this.onBlur,  this);
16261         this.el.relayEvent('keyup', this);
16262
16263         // reference to original value for reset
16264         this.originalValue = this.getValue();
16265         this.resetValue =  this.getValue();
16266     },
16267
16268     // private
16269     onFocus : function(){
16270         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16271             this.el.addClass(this.focusClass);
16272         }
16273         if(!this.hasFocus){
16274             this.hasFocus = true;
16275             this.startValue = this.getValue();
16276             this.fireEvent("focus", this);
16277         }
16278     },
16279
16280     beforeBlur : Roo.emptyFn,
16281
16282     // private
16283     onBlur : function(){
16284         this.beforeBlur();
16285         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16286             this.el.removeClass(this.focusClass);
16287         }
16288         this.hasFocus = false;
16289         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16290             this.validate();
16291         }
16292         var v = this.getValue();
16293         if(String(v) !== String(this.startValue)){
16294             this.fireEvent('change', this, v, this.startValue);
16295         }
16296         this.fireEvent("blur", this);
16297     },
16298
16299     /**
16300      * Returns whether or not the field value is currently valid
16301      * @param {Boolean} preventMark True to disable marking the field invalid
16302      * @return {Boolean} True if the value is valid, else false
16303      */
16304     isValid : function(preventMark){
16305         if(this.disabled){
16306             return true;
16307         }
16308         var restore = this.preventMark;
16309         this.preventMark = preventMark === true;
16310         var v = this.validateValue(this.processValue(this.getRawValue()));
16311         this.preventMark = restore;
16312         return v;
16313     },
16314
16315     /**
16316      * Validates the field value
16317      * @return {Boolean} True if the value is valid, else false
16318      */
16319     validate : function(){
16320         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16321             this.clearInvalid();
16322             return true;
16323         }
16324         return false;
16325     },
16326
16327     processValue : function(value){
16328         return value;
16329     },
16330
16331     // private
16332     // Subclasses should provide the validation implementation by overriding this
16333     validateValue : function(value){
16334         return true;
16335     },
16336
16337     /**
16338      * Mark this field as invalid
16339      * @param {String} msg The validation message
16340      */
16341     markInvalid : function(msg){
16342         if(!this.rendered || this.preventMark){ // not rendered
16343             return;
16344         }
16345         
16346         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16347         
16348         obj.el.addClass(this.invalidClass);
16349         msg = msg || this.invalidText;
16350         switch(this.msgTarget){
16351             case 'qtip':
16352                 obj.el.dom.qtip = msg;
16353                 obj.el.dom.qclass = 'x-form-invalid-tip';
16354                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16355                     Roo.QuickTips.enable();
16356                 }
16357                 break;
16358             case 'title':
16359                 this.el.dom.title = msg;
16360                 break;
16361             case 'under':
16362                 if(!this.errorEl){
16363                     var elp = this.el.findParent('.x-form-element', 5, true);
16364                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16365                     this.errorEl.setWidth(elp.getWidth(true)-20);
16366                 }
16367                 this.errorEl.update(msg);
16368                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16369                 break;
16370             case 'side':
16371                 if(!this.errorIcon){
16372                     var elp = this.el.findParent('.x-form-element', 5, true);
16373                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16374                 }
16375                 this.alignErrorIcon();
16376                 this.errorIcon.dom.qtip = msg;
16377                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16378                 this.errorIcon.show();
16379                 this.on('resize', this.alignErrorIcon, this);
16380                 break;
16381             default:
16382                 var t = Roo.getDom(this.msgTarget);
16383                 t.innerHTML = msg;
16384                 t.style.display = this.msgDisplay;
16385                 break;
16386         }
16387         this.fireEvent('invalid', this, msg);
16388     },
16389
16390     // private
16391     alignErrorIcon : function(){
16392         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16393     },
16394
16395     /**
16396      * Clear any invalid styles/messages for this field
16397      */
16398     clearInvalid : function(){
16399         if(!this.rendered || this.preventMark){ // not rendered
16400             return;
16401         }
16402         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16403         
16404         obj.el.removeClass(this.invalidClass);
16405         switch(this.msgTarget){
16406             case 'qtip':
16407                 obj.el.dom.qtip = '';
16408                 break;
16409             case 'title':
16410                 this.el.dom.title = '';
16411                 break;
16412             case 'under':
16413                 if(this.errorEl){
16414                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16415                 }
16416                 break;
16417             case 'side':
16418                 if(this.errorIcon){
16419                     this.errorIcon.dom.qtip = '';
16420                     this.errorIcon.hide();
16421                     this.un('resize', this.alignErrorIcon, this);
16422                 }
16423                 break;
16424             default:
16425                 var t = Roo.getDom(this.msgTarget);
16426                 t.innerHTML = '';
16427                 t.style.display = 'none';
16428                 break;
16429         }
16430         this.fireEvent('valid', this);
16431     },
16432
16433     /**
16434      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16435      * @return {Mixed} value The field value
16436      */
16437     getRawValue : function(){
16438         var v = this.el.getValue();
16439         
16440         return v;
16441     },
16442
16443     /**
16444      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16445      * @return {Mixed} value The field value
16446      */
16447     getValue : function(){
16448         var v = this.el.getValue();
16449          
16450         return v;
16451     },
16452
16453     /**
16454      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16455      * @param {Mixed} value The value to set
16456      */
16457     setRawValue : function(v){
16458         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16459     },
16460
16461     /**
16462      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16463      * @param {Mixed} value The value to set
16464      */
16465     setValue : function(v){
16466         this.value = v;
16467         if(this.rendered){
16468             this.el.dom.value = (v === null || v === undefined ? '' : v);
16469              this.validate();
16470         }
16471     },
16472
16473     adjustSize : function(w, h){
16474         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16475         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16476         return s;
16477     },
16478
16479     adjustWidth : function(tag, w){
16480         tag = tag.toLowerCase();
16481         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16482             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16483                 if(tag == 'input'){
16484                     return w + 2;
16485                 }
16486                 if(tag == 'textarea'){
16487                     return w-2;
16488                 }
16489             }else if(Roo.isOpera){
16490                 if(tag == 'input'){
16491                     return w + 2;
16492                 }
16493                 if(tag == 'textarea'){
16494                     return w-2;
16495                 }
16496             }
16497         }
16498         return w;
16499     }
16500 });
16501
16502
16503 // anything other than normal should be considered experimental
16504 Roo.form.Field.msgFx = {
16505     normal : {
16506         show: function(msgEl, f){
16507             msgEl.setDisplayed('block');
16508         },
16509
16510         hide : function(msgEl, f){
16511             msgEl.setDisplayed(false).update('');
16512         }
16513     },
16514
16515     slide : {
16516         show: function(msgEl, f){
16517             msgEl.slideIn('t', {stopFx:true});
16518         },
16519
16520         hide : function(msgEl, f){
16521             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16522         }
16523     },
16524
16525     slideRight : {
16526         show: function(msgEl, f){
16527             msgEl.fixDisplay();
16528             msgEl.alignTo(f.el, 'tl-tr');
16529             msgEl.slideIn('l', {stopFx:true});
16530         },
16531
16532         hide : function(msgEl, f){
16533             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16534         }
16535     }
16536 };/*
16537  * Based on:
16538  * Ext JS Library 1.1.1
16539  * Copyright(c) 2006-2007, Ext JS, LLC.
16540  *
16541  * Originally Released Under LGPL - original licence link has changed is not relivant.
16542  *
16543  * Fork - LGPL
16544  * <script type="text/javascript">
16545  */
16546  
16547
16548 /**
16549  * @class Roo.form.TextField
16550  * @extends Roo.form.Field
16551  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16552  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16553  * @constructor
16554  * Creates a new TextField
16555  * @param {Object} config Configuration options
16556  */
16557 Roo.form.TextField = function(config){
16558     Roo.form.TextField.superclass.constructor.call(this, config);
16559     this.addEvents({
16560         /**
16561          * @event autosize
16562          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16563          * according to the default logic, but this event provides a hook for the developer to apply additional
16564          * logic at runtime to resize the field if needed.
16565              * @param {Roo.form.Field} this This text field
16566              * @param {Number} width The new field width
16567              */
16568         autosize : true
16569     });
16570 };
16571
16572 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16573     /**
16574      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16575      */
16576     grow : false,
16577     /**
16578      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16579      */
16580     growMin : 30,
16581     /**
16582      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16583      */
16584     growMax : 800,
16585     /**
16586      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16587      */
16588     vtype : null,
16589     /**
16590      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16591      */
16592     maskRe : null,
16593     /**
16594      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16595      */
16596     disableKeyFilter : false,
16597     /**
16598      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16599      */
16600     allowBlank : true,
16601     /**
16602      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16603      */
16604     minLength : 0,
16605     /**
16606      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16607      */
16608     maxLength : Number.MAX_VALUE,
16609     /**
16610      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16611      */
16612     minLengthText : "The minimum length for this field is {0}",
16613     /**
16614      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16615      */
16616     maxLengthText : "The maximum length for this field is {0}",
16617     /**
16618      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16619      */
16620     selectOnFocus : false,
16621     /**
16622      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
16623      */    
16624     allowLeadingSpace : false,
16625     /**
16626      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16627      */
16628     blankText : "This field is required",
16629     /**
16630      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16631      * If available, this function will be called only after the basic validators all return true, and will be passed the
16632      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16633      */
16634     validator : null,
16635     /**
16636      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16637      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16638      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
16639      */
16640     regex : null,
16641     /**
16642      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16643      */
16644     regexText : "",
16645     /**
16646      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16647      */
16648     emptyText : null,
16649    
16650
16651     // private
16652     initEvents : function()
16653     {
16654         if (this.emptyText) {
16655             this.el.attr('placeholder', this.emptyText);
16656         }
16657         
16658         Roo.form.TextField.superclass.initEvents.call(this);
16659         if(this.validationEvent == 'keyup'){
16660             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16661             this.el.on('keyup', this.filterValidation, this);
16662         }
16663         else if(this.validationEvent !== false){
16664             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16665         }
16666         
16667         if(this.selectOnFocus){
16668             this.on("focus", this.preFocus, this);
16669         }
16670         if (!this.allowLeadingSpace) {
16671             this.on('blur', this.cleanLeadingSpace, this);
16672         }
16673         
16674         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16675             this.el.on("keypress", this.filterKeys, this);
16676         }
16677         if(this.grow){
16678             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
16679             this.el.on("click", this.autoSize,  this);
16680         }
16681         if(this.el.is('input[type=password]') && Roo.isSafari){
16682             this.el.on('keydown', this.SafariOnKeyDown, this);
16683         }
16684     },
16685
16686     processValue : function(value){
16687         if(this.stripCharsRe){
16688             var newValue = value.replace(this.stripCharsRe, '');
16689             if(newValue !== value){
16690                 this.setRawValue(newValue);
16691                 return newValue;
16692             }
16693         }
16694         return value;
16695     },
16696
16697     filterValidation : function(e){
16698         if(!e.isNavKeyPress()){
16699             this.validationTask.delay(this.validationDelay);
16700         }
16701     },
16702
16703     // private
16704     onKeyUp : function(e){
16705         if(!e.isNavKeyPress()){
16706             this.autoSize();
16707         }
16708     },
16709     // private - clean the leading white space
16710     cleanLeadingSpace : function(e)
16711     {
16712         if ( this.inputType == 'file') {
16713             return;
16714         }
16715         
16716         this.setValue((this.getValue() + '').replace(/^\s+/,''));
16717     },
16718     /**
16719      * Resets the current field value to the originally-loaded value and clears any validation messages.
16720      *  
16721      */
16722     reset : function(){
16723         Roo.form.TextField.superclass.reset.call(this);
16724        
16725     }, 
16726     // private
16727     preFocus : function(){
16728         
16729         if(this.selectOnFocus){
16730             this.el.dom.select();
16731         }
16732     },
16733
16734     
16735     // private
16736     filterKeys : function(e){
16737         var k = e.getKey();
16738         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
16739             return;
16740         }
16741         var c = e.getCharCode(), cc = String.fromCharCode(c);
16742         if(Roo.isIE && (e.isSpecialKey() || !cc)){
16743             return;
16744         }
16745         if(!this.maskRe.test(cc)){
16746             e.stopEvent();
16747         }
16748     },
16749
16750     setValue : function(v){
16751         
16752         Roo.form.TextField.superclass.setValue.apply(this, arguments);
16753         
16754         this.autoSize();
16755     },
16756
16757     /**
16758      * Validates a value according to the field's validation rules and marks the field as invalid
16759      * if the validation fails
16760      * @param {Mixed} value The value to validate
16761      * @return {Boolean} True if the value is valid, else false
16762      */
16763     validateValue : function(value){
16764         if(value.length < 1)  { // if it's blank
16765              if(this.allowBlank){
16766                 this.clearInvalid();
16767                 return true;
16768              }else{
16769                 this.markInvalid(this.blankText);
16770                 return false;
16771              }
16772         }
16773         if(value.length < this.minLength){
16774             this.markInvalid(String.format(this.minLengthText, this.minLength));
16775             return false;
16776         }
16777         if(value.length > this.maxLength){
16778             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
16779             return false;
16780         }
16781         if(this.vtype){
16782             var vt = Roo.form.VTypes;
16783             if(!vt[this.vtype](value, this)){
16784                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
16785                 return false;
16786             }
16787         }
16788         if(typeof this.validator == "function"){
16789             var msg = this.validator(value);
16790             if(msg !== true){
16791                 this.markInvalid(msg);
16792                 return false;
16793             }
16794         }
16795         if(this.regex && !this.regex.test(value)){
16796             this.markInvalid(this.regexText);
16797             return false;
16798         }
16799         return true;
16800     },
16801
16802     /**
16803      * Selects text in this field
16804      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
16805      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
16806      */
16807     selectText : function(start, end){
16808         var v = this.getRawValue();
16809         if(v.length > 0){
16810             start = start === undefined ? 0 : start;
16811             end = end === undefined ? v.length : end;
16812             var d = this.el.dom;
16813             if(d.setSelectionRange){
16814                 d.setSelectionRange(start, end);
16815             }else if(d.createTextRange){
16816                 var range = d.createTextRange();
16817                 range.moveStart("character", start);
16818                 range.moveEnd("character", v.length-end);
16819                 range.select();
16820             }
16821         }
16822     },
16823
16824     /**
16825      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
16826      * This only takes effect if grow = true, and fires the autosize event.
16827      */
16828     autoSize : function(){
16829         if(!this.grow || !this.rendered){
16830             return;
16831         }
16832         if(!this.metrics){
16833             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
16834         }
16835         var el = this.el;
16836         var v = el.dom.value;
16837         var d = document.createElement('div');
16838         d.appendChild(document.createTextNode(v));
16839         v = d.innerHTML;
16840         d = null;
16841         v += "&#160;";
16842         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
16843         this.el.setWidth(w);
16844         this.fireEvent("autosize", this, w);
16845     },
16846     
16847     // private
16848     SafariOnKeyDown : function(event)
16849     {
16850         // this is a workaround for a password hang bug on chrome/ webkit.
16851         
16852         var isSelectAll = false;
16853         
16854         if(this.el.dom.selectionEnd > 0){
16855             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
16856         }
16857         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
16858             event.preventDefault();
16859             this.setValue('');
16860             return;
16861         }
16862         
16863         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
16864             
16865             event.preventDefault();
16866             // this is very hacky as keydown always get's upper case.
16867             
16868             var cc = String.fromCharCode(event.getCharCode());
16869             
16870             
16871             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
16872             
16873         }
16874         
16875         
16876     }
16877 });/*
16878  * Based on:
16879  * Ext JS Library 1.1.1
16880  * Copyright(c) 2006-2007, Ext JS, LLC.
16881  *
16882  * Originally Released Under LGPL - original licence link has changed is not relivant.
16883  *
16884  * Fork - LGPL
16885  * <script type="text/javascript">
16886  */
16887  
16888 /**
16889  * @class Roo.form.Hidden
16890  * @extends Roo.form.TextField
16891  * Simple Hidden element used on forms 
16892  * 
16893  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
16894  * 
16895  * @constructor
16896  * Creates a new Hidden form element.
16897  * @param {Object} config Configuration options
16898  */
16899
16900
16901
16902 // easy hidden field...
16903 Roo.form.Hidden = function(config){
16904     Roo.form.Hidden.superclass.constructor.call(this, config);
16905 };
16906   
16907 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
16908     fieldLabel:      '',
16909     inputType:      'hidden',
16910     width:          50,
16911     allowBlank:     true,
16912     labelSeparator: '',
16913     hidden:         true,
16914     itemCls :       'x-form-item-display-none'
16915
16916
16917 });
16918
16919
16920 /*
16921  * Based on:
16922  * Ext JS Library 1.1.1
16923  * Copyright(c) 2006-2007, Ext JS, LLC.
16924  *
16925  * Originally Released Under LGPL - original licence link has changed is not relivant.
16926  *
16927  * Fork - LGPL
16928  * <script type="text/javascript">
16929  */
16930  
16931 /**
16932  * @class Roo.form.TriggerField
16933  * @extends Roo.form.TextField
16934  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
16935  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
16936  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
16937  * for which you can provide a custom implementation.  For example:
16938  * <pre><code>
16939 var trigger = new Roo.form.TriggerField();
16940 trigger.onTriggerClick = myTriggerFn;
16941 trigger.applyTo('my-field');
16942 </code></pre>
16943  *
16944  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
16945  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
16946  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
16947  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
16948  * @constructor
16949  * Create a new TriggerField.
16950  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
16951  * to the base TextField)
16952  */
16953 Roo.form.TriggerField = function(config){
16954     this.mimicing = false;
16955     Roo.form.TriggerField.superclass.constructor.call(this, config);
16956 };
16957
16958 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
16959     /**
16960      * @cfg {String} triggerClass A CSS class to apply to the trigger
16961      */
16962     /**
16963      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16964      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
16965      */
16966     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
16967     /**
16968      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
16969      */
16970     hideTrigger:false,
16971
16972     /** @cfg {Boolean} grow @hide */
16973     /** @cfg {Number} growMin @hide */
16974     /** @cfg {Number} growMax @hide */
16975
16976     /**
16977      * @hide 
16978      * @method
16979      */
16980     autoSize: Roo.emptyFn,
16981     // private
16982     monitorTab : true,
16983     // private
16984     deferHeight : true,
16985
16986     
16987     actionMode : 'wrap',
16988     // private
16989     onResize : function(w, h){
16990         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
16991         if(typeof w == 'number'){
16992             var x = w - this.trigger.getWidth();
16993             this.el.setWidth(this.adjustWidth('input', x));
16994             this.trigger.setStyle('left', x+'px');
16995         }
16996     },
16997
16998     // private
16999     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17000
17001     // private
17002     getResizeEl : function(){
17003         return this.wrap;
17004     },
17005
17006     // private
17007     getPositionEl : function(){
17008         return this.wrap;
17009     },
17010
17011     // private
17012     alignErrorIcon : function(){
17013         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17014     },
17015
17016     // private
17017     onRender : function(ct, position){
17018         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17019         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17020         this.trigger = this.wrap.createChild(this.triggerConfig ||
17021                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17022         if(this.hideTrigger){
17023             this.trigger.setDisplayed(false);
17024         }
17025         this.initTrigger();
17026         if(!this.width){
17027             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17028         }
17029     },
17030
17031     // private
17032     initTrigger : function(){
17033         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17034         this.trigger.addClassOnOver('x-form-trigger-over');
17035         this.trigger.addClassOnClick('x-form-trigger-click');
17036     },
17037
17038     // private
17039     onDestroy : function(){
17040         if(this.trigger){
17041             this.trigger.removeAllListeners();
17042             this.trigger.remove();
17043         }
17044         if(this.wrap){
17045             this.wrap.remove();
17046         }
17047         Roo.form.TriggerField.superclass.onDestroy.call(this);
17048     },
17049
17050     // private
17051     onFocus : function(){
17052         Roo.form.TriggerField.superclass.onFocus.call(this);
17053         if(!this.mimicing){
17054             this.wrap.addClass('x-trigger-wrap-focus');
17055             this.mimicing = true;
17056             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17057             if(this.monitorTab){
17058                 this.el.on("keydown", this.checkTab, this);
17059             }
17060         }
17061     },
17062
17063     // private
17064     checkTab : function(e){
17065         if(e.getKey() == e.TAB){
17066             this.triggerBlur();
17067         }
17068     },
17069
17070     // private
17071     onBlur : function(){
17072         // do nothing
17073     },
17074
17075     // private
17076     mimicBlur : function(e, t){
17077         if(!this.wrap.contains(t) && this.validateBlur()){
17078             this.triggerBlur();
17079         }
17080     },
17081
17082     // private
17083     triggerBlur : function(){
17084         this.mimicing = false;
17085         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17086         if(this.monitorTab){
17087             this.el.un("keydown", this.checkTab, this);
17088         }
17089         this.wrap.removeClass('x-trigger-wrap-focus');
17090         Roo.form.TriggerField.superclass.onBlur.call(this);
17091     },
17092
17093     // private
17094     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17095     validateBlur : function(e, t){
17096         return true;
17097     },
17098
17099     // private
17100     onDisable : function(){
17101         Roo.form.TriggerField.superclass.onDisable.call(this);
17102         if(this.wrap){
17103             this.wrap.addClass('x-item-disabled');
17104         }
17105     },
17106
17107     // private
17108     onEnable : function(){
17109         Roo.form.TriggerField.superclass.onEnable.call(this);
17110         if(this.wrap){
17111             this.wrap.removeClass('x-item-disabled');
17112         }
17113     },
17114
17115     // private
17116     onShow : function(){
17117         var ae = this.getActionEl();
17118         
17119         if(ae){
17120             ae.dom.style.display = '';
17121             ae.dom.style.visibility = 'visible';
17122         }
17123     },
17124
17125     // private
17126     
17127     onHide : function(){
17128         var ae = this.getActionEl();
17129         ae.dom.style.display = 'none';
17130     },
17131
17132     /**
17133      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17134      * by an implementing function.
17135      * @method
17136      * @param {EventObject} e
17137      */
17138     onTriggerClick : Roo.emptyFn
17139 });
17140
17141 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17142 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17143 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17144 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17145     initComponent : function(){
17146         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17147
17148         this.triggerConfig = {
17149             tag:'span', cls:'x-form-twin-triggers', cn:[
17150             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17151             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17152         ]};
17153     },
17154
17155     getTrigger : function(index){
17156         return this.triggers[index];
17157     },
17158
17159     initTrigger : function(){
17160         var ts = this.trigger.select('.x-form-trigger', true);
17161         this.wrap.setStyle('overflow', 'hidden');
17162         var triggerField = this;
17163         ts.each(function(t, all, index){
17164             t.hide = function(){
17165                 var w = triggerField.wrap.getWidth();
17166                 this.dom.style.display = 'none';
17167                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17168             };
17169             t.show = function(){
17170                 var w = triggerField.wrap.getWidth();
17171                 this.dom.style.display = '';
17172                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17173             };
17174             var triggerIndex = 'Trigger'+(index+1);
17175
17176             if(this['hide'+triggerIndex]){
17177                 t.dom.style.display = 'none';
17178             }
17179             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17180             t.addClassOnOver('x-form-trigger-over');
17181             t.addClassOnClick('x-form-trigger-click');
17182         }, this);
17183         this.triggers = ts.elements;
17184     },
17185
17186     onTrigger1Click : Roo.emptyFn,
17187     onTrigger2Click : Roo.emptyFn
17188 });/*
17189  * Based on:
17190  * Ext JS Library 1.1.1
17191  * Copyright(c) 2006-2007, Ext JS, LLC.
17192  *
17193  * Originally Released Under LGPL - original licence link has changed is not relivant.
17194  *
17195  * Fork - LGPL
17196  * <script type="text/javascript">
17197  */
17198  
17199 /**
17200  * @class Roo.form.TextArea
17201  * @extends Roo.form.TextField
17202  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17203  * support for auto-sizing.
17204  * @constructor
17205  * Creates a new TextArea
17206  * @param {Object} config Configuration options
17207  */
17208 Roo.form.TextArea = function(config){
17209     Roo.form.TextArea.superclass.constructor.call(this, config);
17210     // these are provided exchanges for backwards compat
17211     // minHeight/maxHeight were replaced by growMin/growMax to be
17212     // compatible with TextField growing config values
17213     if(this.minHeight !== undefined){
17214         this.growMin = this.minHeight;
17215     }
17216     if(this.maxHeight !== undefined){
17217         this.growMax = this.maxHeight;
17218     }
17219 };
17220
17221 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17222     /**
17223      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17224      */
17225     growMin : 60,
17226     /**
17227      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17228      */
17229     growMax: 1000,
17230     /**
17231      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17232      * in the field (equivalent to setting overflow: hidden, defaults to false)
17233      */
17234     preventScrollbars: false,
17235     /**
17236      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17237      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17238      */
17239
17240     // private
17241     onRender : function(ct, position){
17242         if(!this.el){
17243             this.defaultAutoCreate = {
17244                 tag: "textarea",
17245                 style:"width:300px;height:60px;",
17246                 autocomplete: "new-password"
17247             };
17248         }
17249         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17250         if(this.grow){
17251             this.textSizeEl = Roo.DomHelper.append(document.body, {
17252                 tag: "pre", cls: "x-form-grow-sizer"
17253             });
17254             if(this.preventScrollbars){
17255                 this.el.setStyle("overflow", "hidden");
17256             }
17257             this.el.setHeight(this.growMin);
17258         }
17259     },
17260
17261     onDestroy : function(){
17262         if(this.textSizeEl){
17263             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17264         }
17265         Roo.form.TextArea.superclass.onDestroy.call(this);
17266     },
17267
17268     // private
17269     onKeyUp : function(e){
17270         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17271             this.autoSize();
17272         }
17273     },
17274
17275     /**
17276      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17277      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17278      */
17279     autoSize : function(){
17280         if(!this.grow || !this.textSizeEl){
17281             return;
17282         }
17283         var el = this.el;
17284         var v = el.dom.value;
17285         var ts = this.textSizeEl;
17286
17287         ts.innerHTML = '';
17288         ts.appendChild(document.createTextNode(v));
17289         v = ts.innerHTML;
17290
17291         Roo.fly(ts).setWidth(this.el.getWidth());
17292         if(v.length < 1){
17293             v = "&#160;&#160;";
17294         }else{
17295             if(Roo.isIE){
17296                 v = v.replace(/\n/g, '<p>&#160;</p>');
17297             }
17298             v += "&#160;\n&#160;";
17299         }
17300         ts.innerHTML = v;
17301         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17302         if(h != this.lastHeight){
17303             this.lastHeight = h;
17304             this.el.setHeight(h);
17305             this.fireEvent("autosize", this, h);
17306         }
17307     }
17308 });/*
17309  * Based on:
17310  * Ext JS Library 1.1.1
17311  * Copyright(c) 2006-2007, Ext JS, LLC.
17312  *
17313  * Originally Released Under LGPL - original licence link has changed is not relivant.
17314  *
17315  * Fork - LGPL
17316  * <script type="text/javascript">
17317  */
17318  
17319
17320 /**
17321  * @class Roo.form.NumberField
17322  * @extends Roo.form.TextField
17323  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17324  * @constructor
17325  * Creates a new NumberField
17326  * @param {Object} config Configuration options
17327  */
17328 Roo.form.NumberField = function(config){
17329     Roo.form.NumberField.superclass.constructor.call(this, config);
17330 };
17331
17332 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17333     /**
17334      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17335      */
17336     fieldClass: "x-form-field x-form-num-field",
17337     /**
17338      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17339      */
17340     allowDecimals : true,
17341     /**
17342      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17343      */
17344     decimalSeparator : ".",
17345     /**
17346      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17347      */
17348     decimalPrecision : 2,
17349     /**
17350      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17351      */
17352     allowNegative : true,
17353     /**
17354      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17355      */
17356     minValue : Number.NEGATIVE_INFINITY,
17357     /**
17358      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17359      */
17360     maxValue : Number.MAX_VALUE,
17361     /**
17362      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17363      */
17364     minText : "The minimum value for this field is {0}",
17365     /**
17366      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17367      */
17368     maxText : "The maximum value for this field is {0}",
17369     /**
17370      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17371      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17372      */
17373     nanText : "{0} is not a valid number",
17374
17375     // private
17376     initEvents : function(){
17377         Roo.form.NumberField.superclass.initEvents.call(this);
17378         var allowed = "0123456789";
17379         if(this.allowDecimals){
17380             allowed += this.decimalSeparator;
17381         }
17382         if(this.allowNegative){
17383             allowed += "-";
17384         }
17385         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17386         var keyPress = function(e){
17387             var k = e.getKey();
17388             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17389                 return;
17390             }
17391             var c = e.getCharCode();
17392             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17393                 e.stopEvent();
17394             }
17395         };
17396         this.el.on("keypress", keyPress, this);
17397     },
17398
17399     // private
17400     validateValue : function(value){
17401         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17402             return false;
17403         }
17404         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17405              return true;
17406         }
17407         var num = this.parseValue(value);
17408         if(isNaN(num)){
17409             this.markInvalid(String.format(this.nanText, value));
17410             return false;
17411         }
17412         if(num < this.minValue){
17413             this.markInvalid(String.format(this.minText, this.minValue));
17414             return false;
17415         }
17416         if(num > this.maxValue){
17417             this.markInvalid(String.format(this.maxText, this.maxValue));
17418             return false;
17419         }
17420         return true;
17421     },
17422
17423     getValue : function(){
17424         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17425     },
17426
17427     // private
17428     parseValue : function(value){
17429         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17430         return isNaN(value) ? '' : value;
17431     },
17432
17433     // private
17434     fixPrecision : function(value){
17435         var nan = isNaN(value);
17436         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17437             return nan ? '' : value;
17438         }
17439         return parseFloat(value).toFixed(this.decimalPrecision);
17440     },
17441
17442     setValue : function(v){
17443         v = this.fixPrecision(v);
17444         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17445     },
17446
17447     // private
17448     decimalPrecisionFcn : function(v){
17449         return Math.floor(v);
17450     },
17451
17452     beforeBlur : function(){
17453         var v = this.parseValue(this.getRawValue());
17454         if(v){
17455             this.setValue(v);
17456         }
17457     }
17458 });/*
17459  * Based on:
17460  * Ext JS Library 1.1.1
17461  * Copyright(c) 2006-2007, Ext JS, LLC.
17462  *
17463  * Originally Released Under LGPL - original licence link has changed is not relivant.
17464  *
17465  * Fork - LGPL
17466  * <script type="text/javascript">
17467  */
17468  
17469 /**
17470  * @class Roo.form.DateField
17471  * @extends Roo.form.TriggerField
17472  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17473 * @constructor
17474 * Create a new DateField
17475 * @param {Object} config
17476  */
17477 Roo.form.DateField = function(config)
17478 {
17479     Roo.form.DateField.superclass.constructor.call(this, config);
17480     
17481       this.addEvents({
17482          
17483         /**
17484          * @event select
17485          * Fires when a date is selected
17486              * @param {Roo.form.DateField} combo This combo box
17487              * @param {Date} date The date selected
17488              */
17489         'select' : true
17490          
17491     });
17492     
17493     
17494     if(typeof this.minValue == "string") {
17495         this.minValue = this.parseDate(this.minValue);
17496     }
17497     if(typeof this.maxValue == "string") {
17498         this.maxValue = this.parseDate(this.maxValue);
17499     }
17500     this.ddMatch = null;
17501     if(this.disabledDates){
17502         var dd = this.disabledDates;
17503         var re = "(?:";
17504         for(var i = 0; i < dd.length; i++){
17505             re += dd[i];
17506             if(i != dd.length-1) {
17507                 re += "|";
17508             }
17509         }
17510         this.ddMatch = new RegExp(re + ")");
17511     }
17512 };
17513
17514 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17515     /**
17516      * @cfg {String} format
17517      * The default date format string which can be overriden for localization support.  The format must be
17518      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17519      */
17520     format : "m/d/y",
17521     /**
17522      * @cfg {String} altFormats
17523      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17524      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17525      */
17526     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17527     /**
17528      * @cfg {Array} disabledDays
17529      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17530      */
17531     disabledDays : null,
17532     /**
17533      * @cfg {String} disabledDaysText
17534      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17535      */
17536     disabledDaysText : "Disabled",
17537     /**
17538      * @cfg {Array} disabledDates
17539      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17540      * expression so they are very powerful. Some examples:
17541      * <ul>
17542      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17543      * <li>["03/08", "09/16"] would disable those days for every year</li>
17544      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17545      * <li>["03/../2006"] would disable every day in March 2006</li>
17546      * <li>["^03"] would disable every day in every March</li>
17547      * </ul>
17548      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17549      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17550      */
17551     disabledDates : null,
17552     /**
17553      * @cfg {String} disabledDatesText
17554      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17555      */
17556     disabledDatesText : "Disabled",
17557     /**
17558      * @cfg {Date/String} minValue
17559      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17560      * valid format (defaults to null).
17561      */
17562     minValue : null,
17563     /**
17564      * @cfg {Date/String} maxValue
17565      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17566      * valid format (defaults to null).
17567      */
17568     maxValue : null,
17569     /**
17570      * @cfg {String} minText
17571      * The error text to display when the date in the cell is before minValue (defaults to
17572      * 'The date in this field must be after {minValue}').
17573      */
17574     minText : "The date in this field must be equal to or after {0}",
17575     /**
17576      * @cfg {String} maxText
17577      * The error text to display when the date in the cell is after maxValue (defaults to
17578      * 'The date in this field must be before {maxValue}').
17579      */
17580     maxText : "The date in this field must be equal to or before {0}",
17581     /**
17582      * @cfg {String} invalidText
17583      * The error text to display when the date in the field is invalid (defaults to
17584      * '{value} is not a valid date - it must be in the format {format}').
17585      */
17586     invalidText : "{0} is not a valid date - it must be in the format {1}",
17587     /**
17588      * @cfg {String} triggerClass
17589      * An additional CSS class used to style the trigger button.  The trigger will always get the
17590      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17591      * which displays a calendar icon).
17592      */
17593     triggerClass : 'x-form-date-trigger',
17594     
17595
17596     /**
17597      * @cfg {Boolean} useIso
17598      * if enabled, then the date field will use a hidden field to store the 
17599      * real value as iso formated date. default (false)
17600      */ 
17601     useIso : false,
17602     /**
17603      * @cfg {String/Object} autoCreate
17604      * A DomHelper element spec, or true for a default element spec (defaults to
17605      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17606      */ 
17607     // private
17608     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17609     
17610     // private
17611     hiddenField: false,
17612     
17613     onRender : function(ct, position)
17614     {
17615         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17616         if (this.useIso) {
17617             //this.el.dom.removeAttribute('name'); 
17618             Roo.log("Changing name?");
17619             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17620             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17621                     'before', true);
17622             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17623             // prevent input submission
17624             this.hiddenName = this.name;
17625         }
17626             
17627             
17628     },
17629     
17630     // private
17631     validateValue : function(value)
17632     {
17633         value = this.formatDate(value);
17634         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17635             Roo.log('super failed');
17636             return false;
17637         }
17638         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17639              return true;
17640         }
17641         var svalue = value;
17642         value = this.parseDate(value);
17643         if(!value){
17644             Roo.log('parse date failed' + svalue);
17645             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17646             return false;
17647         }
17648         var time = value.getTime();
17649         if(this.minValue && time < this.minValue.getTime()){
17650             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17651             return false;
17652         }
17653         if(this.maxValue && time > this.maxValue.getTime()){
17654             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17655             return false;
17656         }
17657         if(this.disabledDays){
17658             var day = value.getDay();
17659             for(var i = 0; i < this.disabledDays.length; i++) {
17660                 if(day === this.disabledDays[i]){
17661                     this.markInvalid(this.disabledDaysText);
17662                     return false;
17663                 }
17664             }
17665         }
17666         var fvalue = this.formatDate(value);
17667         if(this.ddMatch && this.ddMatch.test(fvalue)){
17668             this.markInvalid(String.format(this.disabledDatesText, fvalue));
17669             return false;
17670         }
17671         return true;
17672     },
17673
17674     // private
17675     // Provides logic to override the default TriggerField.validateBlur which just returns true
17676     validateBlur : function(){
17677         return !this.menu || !this.menu.isVisible();
17678     },
17679     
17680     getName: function()
17681     {
17682         // returns hidden if it's set..
17683         if (!this.rendered) {return ''};
17684         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
17685         
17686     },
17687
17688     /**
17689      * Returns the current date value of the date field.
17690      * @return {Date} The date value
17691      */
17692     getValue : function(){
17693         
17694         return  this.hiddenField ?
17695                 this.hiddenField.value :
17696                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17697     },
17698
17699     /**
17700      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
17701      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17702      * (the default format used is "m/d/y").
17703      * <br />Usage:
17704      * <pre><code>
17705 //All of these calls set the same date value (May 4, 2006)
17706
17707 //Pass a date object:
17708 var dt = new Date('5/4/06');
17709 dateField.setValue(dt);
17710
17711 //Pass a date string (default format):
17712 dateField.setValue('5/4/06');
17713
17714 //Pass a date string (custom format):
17715 dateField.format = 'Y-m-d';
17716 dateField.setValue('2006-5-4');
17717 </code></pre>
17718      * @param {String/Date} date The date or valid date string
17719      */
17720     setValue : function(date){
17721         if (this.hiddenField) {
17722             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
17723         }
17724         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
17725         // make sure the value field is always stored as a date..
17726         this.value = this.parseDate(date);
17727         
17728         
17729     },
17730
17731     // private
17732     parseDate : function(value){
17733         if(!value || value instanceof Date){
17734             return value;
17735         }
17736         var v = Date.parseDate(value, this.format);
17737          if (!v && this.useIso) {
17738             v = Date.parseDate(value, 'Y-m-d');
17739         }
17740         if(!v && this.altFormats){
17741             if(!this.altFormatsArray){
17742                 this.altFormatsArray = this.altFormats.split("|");
17743             }
17744             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17745                 v = Date.parseDate(value, this.altFormatsArray[i]);
17746             }
17747         }
17748         return v;
17749     },
17750
17751     // private
17752     formatDate : function(date, fmt){
17753         return (!date || !(date instanceof Date)) ?
17754                date : date.dateFormat(fmt || this.format);
17755     },
17756
17757     // private
17758     menuListeners : {
17759         select: function(m, d){
17760             
17761             this.setValue(d);
17762             this.fireEvent('select', this, d);
17763         },
17764         show : function(){ // retain focus styling
17765             this.onFocus();
17766         },
17767         hide : function(){
17768             this.focus.defer(10, this);
17769             var ml = this.menuListeners;
17770             this.menu.un("select", ml.select,  this);
17771             this.menu.un("show", ml.show,  this);
17772             this.menu.un("hide", ml.hide,  this);
17773         }
17774     },
17775
17776     // private
17777     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
17778     onTriggerClick : function(){
17779         if(this.disabled){
17780             return;
17781         }
17782         if(this.menu == null){
17783             this.menu = new Roo.menu.DateMenu();
17784         }
17785         Roo.apply(this.menu.picker,  {
17786             showClear: this.allowBlank,
17787             minDate : this.minValue,
17788             maxDate : this.maxValue,
17789             disabledDatesRE : this.ddMatch,
17790             disabledDatesText : this.disabledDatesText,
17791             disabledDays : this.disabledDays,
17792             disabledDaysText : this.disabledDaysText,
17793             format : this.useIso ? 'Y-m-d' : this.format,
17794             minText : String.format(this.minText, this.formatDate(this.minValue)),
17795             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
17796         });
17797         this.menu.on(Roo.apply({}, this.menuListeners, {
17798             scope:this
17799         }));
17800         this.menu.picker.setValue(this.getValue() || new Date());
17801         this.menu.show(this.el, "tl-bl?");
17802     },
17803
17804     beforeBlur : function(){
17805         var v = this.parseDate(this.getRawValue());
17806         if(v){
17807             this.setValue(v);
17808         }
17809     },
17810
17811     /*@
17812      * overide
17813      * 
17814      */
17815     isDirty : function() {
17816         if(this.disabled) {
17817             return false;
17818         }
17819         
17820         if(typeof(this.startValue) === 'undefined'){
17821             return false;
17822         }
17823         
17824         return String(this.getValue()) !== String(this.startValue);
17825         
17826     },
17827     // @overide
17828     cleanLeadingSpace : function(e)
17829     {
17830        return;
17831     }
17832     
17833 });/*
17834  * Based on:
17835  * Ext JS Library 1.1.1
17836  * Copyright(c) 2006-2007, Ext JS, LLC.
17837  *
17838  * Originally Released Under LGPL - original licence link has changed is not relivant.
17839  *
17840  * Fork - LGPL
17841  * <script type="text/javascript">
17842  */
17843  
17844 /**
17845  * @class Roo.form.MonthField
17846  * @extends Roo.form.TriggerField
17847  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17848 * @constructor
17849 * Create a new MonthField
17850 * @param {Object} config
17851  */
17852 Roo.form.MonthField = function(config){
17853     
17854     Roo.form.MonthField.superclass.constructor.call(this, config);
17855     
17856       this.addEvents({
17857          
17858         /**
17859          * @event select
17860          * Fires when a date is selected
17861              * @param {Roo.form.MonthFieeld} combo This combo box
17862              * @param {Date} date The date selected
17863              */
17864         'select' : true
17865          
17866     });
17867     
17868     
17869     if(typeof this.minValue == "string") {
17870         this.minValue = this.parseDate(this.minValue);
17871     }
17872     if(typeof this.maxValue == "string") {
17873         this.maxValue = this.parseDate(this.maxValue);
17874     }
17875     this.ddMatch = null;
17876     if(this.disabledDates){
17877         var dd = this.disabledDates;
17878         var re = "(?:";
17879         for(var i = 0; i < dd.length; i++){
17880             re += dd[i];
17881             if(i != dd.length-1) {
17882                 re += "|";
17883             }
17884         }
17885         this.ddMatch = new RegExp(re + ")");
17886     }
17887 };
17888
17889 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
17890     /**
17891      * @cfg {String} format
17892      * The default date format string which can be overriden for localization support.  The format must be
17893      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17894      */
17895     format : "M Y",
17896     /**
17897      * @cfg {String} altFormats
17898      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17899      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17900      */
17901     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
17902     /**
17903      * @cfg {Array} disabledDays
17904      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17905      */
17906     disabledDays : [0,1,2,3,4,5,6],
17907     /**
17908      * @cfg {String} disabledDaysText
17909      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17910      */
17911     disabledDaysText : "Disabled",
17912     /**
17913      * @cfg {Array} disabledDates
17914      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17915      * expression so they are very powerful. Some examples:
17916      * <ul>
17917      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17918      * <li>["03/08", "09/16"] would disable those days for every year</li>
17919      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17920      * <li>["03/../2006"] would disable every day in March 2006</li>
17921      * <li>["^03"] would disable every day in every March</li>
17922      * </ul>
17923      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17924      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17925      */
17926     disabledDates : null,
17927     /**
17928      * @cfg {String} disabledDatesText
17929      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17930      */
17931     disabledDatesText : "Disabled",
17932     /**
17933      * @cfg {Date/String} minValue
17934      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17935      * valid format (defaults to null).
17936      */
17937     minValue : null,
17938     /**
17939      * @cfg {Date/String} maxValue
17940      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17941      * valid format (defaults to null).
17942      */
17943     maxValue : null,
17944     /**
17945      * @cfg {String} minText
17946      * The error text to display when the date in the cell is before minValue (defaults to
17947      * 'The date in this field must be after {minValue}').
17948      */
17949     minText : "The date in this field must be equal to or after {0}",
17950     /**
17951      * @cfg {String} maxTextf
17952      * The error text to display when the date in the cell is after maxValue (defaults to
17953      * 'The date in this field must be before {maxValue}').
17954      */
17955     maxText : "The date in this field must be equal to or before {0}",
17956     /**
17957      * @cfg {String} invalidText
17958      * The error text to display when the date in the field is invalid (defaults to
17959      * '{value} is not a valid date - it must be in the format {format}').
17960      */
17961     invalidText : "{0} is not a valid date - it must be in the format {1}",
17962     /**
17963      * @cfg {String} triggerClass
17964      * An additional CSS class used to style the trigger button.  The trigger will always get the
17965      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17966      * which displays a calendar icon).
17967      */
17968     triggerClass : 'x-form-date-trigger',
17969     
17970
17971     /**
17972      * @cfg {Boolean} useIso
17973      * if enabled, then the date field will use a hidden field to store the 
17974      * real value as iso formated date. default (true)
17975      */ 
17976     useIso : true,
17977     /**
17978      * @cfg {String/Object} autoCreate
17979      * A DomHelper element spec, or true for a default element spec (defaults to
17980      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17981      */ 
17982     // private
17983     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
17984     
17985     // private
17986     hiddenField: false,
17987     
17988     hideMonthPicker : false,
17989     
17990     onRender : function(ct, position)
17991     {
17992         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
17993         if (this.useIso) {
17994             this.el.dom.removeAttribute('name'); 
17995             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17996                     'before', true);
17997             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17998             // prevent input submission
17999             this.hiddenName = this.name;
18000         }
18001             
18002             
18003     },
18004     
18005     // private
18006     validateValue : function(value)
18007     {
18008         value = this.formatDate(value);
18009         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18010             return false;
18011         }
18012         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18013              return true;
18014         }
18015         var svalue = value;
18016         value = this.parseDate(value);
18017         if(!value){
18018             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18019             return false;
18020         }
18021         var time = value.getTime();
18022         if(this.minValue && time < this.minValue.getTime()){
18023             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18024             return false;
18025         }
18026         if(this.maxValue && time > this.maxValue.getTime()){
18027             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18028             return false;
18029         }
18030         /*if(this.disabledDays){
18031             var day = value.getDay();
18032             for(var i = 0; i < this.disabledDays.length; i++) {
18033                 if(day === this.disabledDays[i]){
18034                     this.markInvalid(this.disabledDaysText);
18035                     return false;
18036                 }
18037             }
18038         }
18039         */
18040         var fvalue = this.formatDate(value);
18041         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18042             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18043             return false;
18044         }
18045         */
18046         return true;
18047     },
18048
18049     // private
18050     // Provides logic to override the default TriggerField.validateBlur which just returns true
18051     validateBlur : function(){
18052         return !this.menu || !this.menu.isVisible();
18053     },
18054
18055     /**
18056      * Returns the current date value of the date field.
18057      * @return {Date} The date value
18058      */
18059     getValue : function(){
18060         
18061         
18062         
18063         return  this.hiddenField ?
18064                 this.hiddenField.value :
18065                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18066     },
18067
18068     /**
18069      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18070      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18071      * (the default format used is "m/d/y").
18072      * <br />Usage:
18073      * <pre><code>
18074 //All of these calls set the same date value (May 4, 2006)
18075
18076 //Pass a date object:
18077 var dt = new Date('5/4/06');
18078 monthField.setValue(dt);
18079
18080 //Pass a date string (default format):
18081 monthField.setValue('5/4/06');
18082
18083 //Pass a date string (custom format):
18084 monthField.format = 'Y-m-d';
18085 monthField.setValue('2006-5-4');
18086 </code></pre>
18087      * @param {String/Date} date The date or valid date string
18088      */
18089     setValue : function(date){
18090         Roo.log('month setValue' + date);
18091         // can only be first of month..
18092         
18093         var val = this.parseDate(date);
18094         
18095         if (this.hiddenField) {
18096             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18097         }
18098         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18099         this.value = this.parseDate(date);
18100     },
18101
18102     // private
18103     parseDate : function(value){
18104         if(!value || value instanceof Date){
18105             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18106             return value;
18107         }
18108         var v = Date.parseDate(value, this.format);
18109         if (!v && this.useIso) {
18110             v = Date.parseDate(value, 'Y-m-d');
18111         }
18112         if (v) {
18113             // 
18114             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18115         }
18116         
18117         
18118         if(!v && this.altFormats){
18119             if(!this.altFormatsArray){
18120                 this.altFormatsArray = this.altFormats.split("|");
18121             }
18122             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18123                 v = Date.parseDate(value, this.altFormatsArray[i]);
18124             }
18125         }
18126         return v;
18127     },
18128
18129     // private
18130     formatDate : function(date, fmt){
18131         return (!date || !(date instanceof Date)) ?
18132                date : date.dateFormat(fmt || this.format);
18133     },
18134
18135     // private
18136     menuListeners : {
18137         select: function(m, d){
18138             this.setValue(d);
18139             this.fireEvent('select', this, d);
18140         },
18141         show : function(){ // retain focus styling
18142             this.onFocus();
18143         },
18144         hide : function(){
18145             this.focus.defer(10, this);
18146             var ml = this.menuListeners;
18147             this.menu.un("select", ml.select,  this);
18148             this.menu.un("show", ml.show,  this);
18149             this.menu.un("hide", ml.hide,  this);
18150         }
18151     },
18152     // private
18153     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18154     onTriggerClick : function(){
18155         if(this.disabled){
18156             return;
18157         }
18158         if(this.menu == null){
18159             this.menu = new Roo.menu.DateMenu();
18160            
18161         }
18162         
18163         Roo.apply(this.menu.picker,  {
18164             
18165             showClear: this.allowBlank,
18166             minDate : this.minValue,
18167             maxDate : this.maxValue,
18168             disabledDatesRE : this.ddMatch,
18169             disabledDatesText : this.disabledDatesText,
18170             
18171             format : this.useIso ? 'Y-m-d' : this.format,
18172             minText : String.format(this.minText, this.formatDate(this.minValue)),
18173             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18174             
18175         });
18176          this.menu.on(Roo.apply({}, this.menuListeners, {
18177             scope:this
18178         }));
18179        
18180         
18181         var m = this.menu;
18182         var p = m.picker;
18183         
18184         // hide month picker get's called when we called by 'before hide';
18185         
18186         var ignorehide = true;
18187         p.hideMonthPicker  = function(disableAnim){
18188             if (ignorehide) {
18189                 return;
18190             }
18191              if(this.monthPicker){
18192                 Roo.log("hideMonthPicker called");
18193                 if(disableAnim === true){
18194                     this.monthPicker.hide();
18195                 }else{
18196                     this.monthPicker.slideOut('t', {duration:.2});
18197                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18198                     p.fireEvent("select", this, this.value);
18199                     m.hide();
18200                 }
18201             }
18202         }
18203         
18204         Roo.log('picker set value');
18205         Roo.log(this.getValue());
18206         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18207         m.show(this.el, 'tl-bl?');
18208         ignorehide  = false;
18209         // this will trigger hideMonthPicker..
18210         
18211         
18212         // hidden the day picker
18213         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18214         
18215         
18216         
18217       
18218         
18219         p.showMonthPicker.defer(100, p);
18220     
18221         
18222        
18223     },
18224
18225     beforeBlur : function(){
18226         var v = this.parseDate(this.getRawValue());
18227         if(v){
18228             this.setValue(v);
18229         }
18230     }
18231
18232     /** @cfg {Boolean} grow @hide */
18233     /** @cfg {Number} growMin @hide */
18234     /** @cfg {Number} growMax @hide */
18235     /**
18236      * @hide
18237      * @method autoSize
18238      */
18239 });/*
18240  * Based on:
18241  * Ext JS Library 1.1.1
18242  * Copyright(c) 2006-2007, Ext JS, LLC.
18243  *
18244  * Originally Released Under LGPL - original licence link has changed is not relivant.
18245  *
18246  * Fork - LGPL
18247  * <script type="text/javascript">
18248  */
18249  
18250
18251 /**
18252  * @class Roo.form.ComboBox
18253  * @extends Roo.form.TriggerField
18254  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18255  * @constructor
18256  * Create a new ComboBox.
18257  * @param {Object} config Configuration options
18258  */
18259 Roo.form.ComboBox = function(config){
18260     Roo.form.ComboBox.superclass.constructor.call(this, config);
18261     this.addEvents({
18262         /**
18263          * @event expand
18264          * Fires when the dropdown list is expanded
18265              * @param {Roo.form.ComboBox} combo This combo box
18266              */
18267         'expand' : true,
18268         /**
18269          * @event collapse
18270          * Fires when the dropdown list is collapsed
18271              * @param {Roo.form.ComboBox} combo This combo box
18272              */
18273         'collapse' : true,
18274         /**
18275          * @event beforeselect
18276          * Fires before a list item is selected. Return false to cancel the selection.
18277              * @param {Roo.form.ComboBox} combo This combo box
18278              * @param {Roo.data.Record} record The data record returned from the underlying store
18279              * @param {Number} index The index of the selected item in the dropdown list
18280              */
18281         'beforeselect' : true,
18282         /**
18283          * @event select
18284          * Fires when a list item is selected
18285              * @param {Roo.form.ComboBox} combo This combo box
18286              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18287              * @param {Number} index The index of the selected item in the dropdown list
18288              */
18289         'select' : true,
18290         /**
18291          * @event beforequery
18292          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18293          * The event object passed has these properties:
18294              * @param {Roo.form.ComboBox} combo This combo box
18295              * @param {String} query The query
18296              * @param {Boolean} forceAll true to force "all" query
18297              * @param {Boolean} cancel true to cancel the query
18298              * @param {Object} e The query event object
18299              */
18300         'beforequery': true,
18301          /**
18302          * @event add
18303          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18304              * @param {Roo.form.ComboBox} combo This combo box
18305              */
18306         'add' : true,
18307         /**
18308          * @event edit
18309          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18310              * @param {Roo.form.ComboBox} combo This combo box
18311              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18312              */
18313         'edit' : true
18314         
18315         
18316     });
18317     if(this.transform){
18318         this.allowDomMove = false;
18319         var s = Roo.getDom(this.transform);
18320         if(!this.hiddenName){
18321             this.hiddenName = s.name;
18322         }
18323         if(!this.store){
18324             this.mode = 'local';
18325             var d = [], opts = s.options;
18326             for(var i = 0, len = opts.length;i < len; i++){
18327                 var o = opts[i];
18328                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18329                 if(o.selected) {
18330                     this.value = value;
18331                 }
18332                 d.push([value, o.text]);
18333             }
18334             this.store = new Roo.data.SimpleStore({
18335                 'id': 0,
18336                 fields: ['value', 'text'],
18337                 data : d
18338             });
18339             this.valueField = 'value';
18340             this.displayField = 'text';
18341         }
18342         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18343         if(!this.lazyRender){
18344             this.target = true;
18345             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18346             s.parentNode.removeChild(s); // remove it
18347             this.render(this.el.parentNode);
18348         }else{
18349             s.parentNode.removeChild(s); // remove it
18350         }
18351
18352     }
18353     if (this.store) {
18354         this.store = Roo.factory(this.store, Roo.data);
18355     }
18356     
18357     this.selectedIndex = -1;
18358     if(this.mode == 'local'){
18359         if(config.queryDelay === undefined){
18360             this.queryDelay = 10;
18361         }
18362         if(config.minChars === undefined){
18363             this.minChars = 0;
18364         }
18365     }
18366 };
18367
18368 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18369     /**
18370      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18371      */
18372     /**
18373      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18374      * rendering into an Roo.Editor, defaults to false)
18375      */
18376     /**
18377      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18378      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18379      */
18380     /**
18381      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18382      */
18383     /**
18384      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18385      * the dropdown list (defaults to undefined, with no header element)
18386      */
18387
18388      /**
18389      * @cfg {String/Roo.Template} tpl The template to use to render the output
18390      */
18391      
18392     // private
18393     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18394     /**
18395      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18396      */
18397     listWidth: undefined,
18398     /**
18399      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18400      * mode = 'remote' or 'text' if mode = 'local')
18401      */
18402     displayField: undefined,
18403     /**
18404      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18405      * mode = 'remote' or 'value' if mode = 'local'). 
18406      * Note: use of a valueField requires the user make a selection
18407      * in order for a value to be mapped.
18408      */
18409     valueField: undefined,
18410     
18411     
18412     /**
18413      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18414      * field's data value (defaults to the underlying DOM element's name)
18415      */
18416     hiddenName: undefined,
18417     /**
18418      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18419      */
18420     listClass: '',
18421     /**
18422      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18423      */
18424     selectedClass: 'x-combo-selected',
18425     /**
18426      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18427      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18428      * which displays a downward arrow icon).
18429      */
18430     triggerClass : 'x-form-arrow-trigger',
18431     /**
18432      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18433      */
18434     shadow:'sides',
18435     /**
18436      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18437      * anchor positions (defaults to 'tl-bl')
18438      */
18439     listAlign: 'tl-bl?',
18440     /**
18441      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18442      */
18443     maxHeight: 300,
18444     /**
18445      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18446      * query specified by the allQuery config option (defaults to 'query')
18447      */
18448     triggerAction: 'query',
18449     /**
18450      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18451      * (defaults to 4, does not apply if editable = false)
18452      */
18453     minChars : 4,
18454     /**
18455      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18456      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18457      */
18458     typeAhead: false,
18459     /**
18460      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18461      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18462      */
18463     queryDelay: 500,
18464     /**
18465      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18466      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18467      */
18468     pageSize: 0,
18469     /**
18470      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18471      * when editable = true (defaults to false)
18472      */
18473     selectOnFocus:false,
18474     /**
18475      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18476      */
18477     queryParam: 'query',
18478     /**
18479      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18480      * when mode = 'remote' (defaults to 'Loading...')
18481      */
18482     loadingText: 'Loading...',
18483     /**
18484      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18485      */
18486     resizable: false,
18487     /**
18488      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18489      */
18490     handleHeight : 8,
18491     /**
18492      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18493      * traditional select (defaults to true)
18494      */
18495     editable: true,
18496     /**
18497      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18498      */
18499     allQuery: '',
18500     /**
18501      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18502      */
18503     mode: 'remote',
18504     /**
18505      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18506      * listWidth has a higher value)
18507      */
18508     minListWidth : 70,
18509     /**
18510      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18511      * allow the user to set arbitrary text into the field (defaults to false)
18512      */
18513     forceSelection:false,
18514     /**
18515      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18516      * if typeAhead = true (defaults to 250)
18517      */
18518     typeAheadDelay : 250,
18519     /**
18520      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18521      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18522      */
18523     valueNotFoundText : undefined,
18524     /**
18525      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18526      */
18527     blockFocus : false,
18528     
18529     /**
18530      * @cfg {Boolean} disableClear Disable showing of clear button.
18531      */
18532     disableClear : false,
18533     /**
18534      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18535      */
18536     alwaysQuery : false,
18537     
18538     //private
18539     addicon : false,
18540     editicon: false,
18541     
18542     // element that contains real text value.. (when hidden is used..)
18543      
18544     // private
18545     onRender : function(ct, position)
18546     {
18547         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18548         
18549         if(this.hiddenName){
18550             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18551                     'before', true);
18552             this.hiddenField.value =
18553                 this.hiddenValue !== undefined ? this.hiddenValue :
18554                 this.value !== undefined ? this.value : '';
18555
18556             // prevent input submission
18557             this.el.dom.removeAttribute('name');
18558              
18559              
18560         }
18561         
18562         if(Roo.isGecko){
18563             this.el.dom.setAttribute('autocomplete', 'off');
18564         }
18565
18566         var cls = 'x-combo-list';
18567
18568         this.list = new Roo.Layer({
18569             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18570         });
18571
18572         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18573         this.list.setWidth(lw);
18574         this.list.swallowEvent('mousewheel');
18575         this.assetHeight = 0;
18576
18577         if(this.title){
18578             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18579             this.assetHeight += this.header.getHeight();
18580         }
18581
18582         this.innerList = this.list.createChild({cls:cls+'-inner'});
18583         this.innerList.on('mouseover', this.onViewOver, this);
18584         this.innerList.on('mousemove', this.onViewMove, this);
18585         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18586         
18587         if(this.allowBlank && !this.pageSize && !this.disableClear){
18588             this.footer = this.list.createChild({cls:cls+'-ft'});
18589             this.pageTb = new Roo.Toolbar(this.footer);
18590            
18591         }
18592         if(this.pageSize){
18593             this.footer = this.list.createChild({cls:cls+'-ft'});
18594             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18595                     {pageSize: this.pageSize});
18596             
18597         }
18598         
18599         if (this.pageTb && this.allowBlank && !this.disableClear) {
18600             var _this = this;
18601             this.pageTb.add(new Roo.Toolbar.Fill(), {
18602                 cls: 'x-btn-icon x-btn-clear',
18603                 text: '&#160;',
18604                 handler: function()
18605                 {
18606                     _this.collapse();
18607                     _this.clearValue();
18608                     _this.onSelect(false, -1);
18609                 }
18610             });
18611         }
18612         if (this.footer) {
18613             this.assetHeight += this.footer.getHeight();
18614         }
18615         
18616
18617         if(!this.tpl){
18618             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18619         }
18620
18621         this.view = new Roo.View(this.innerList, this.tpl, {
18622             singleSelect:true,
18623             store: this.store,
18624             selectedClass: this.selectedClass
18625         });
18626
18627         this.view.on('click', this.onViewClick, this);
18628
18629         this.store.on('beforeload', this.onBeforeLoad, this);
18630         this.store.on('load', this.onLoad, this);
18631         this.store.on('loadexception', this.onLoadException, this);
18632
18633         if(this.resizable){
18634             this.resizer = new Roo.Resizable(this.list,  {
18635                pinned:true, handles:'se'
18636             });
18637             this.resizer.on('resize', function(r, w, h){
18638                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18639                 this.listWidth = w;
18640                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18641                 this.restrictHeight();
18642             }, this);
18643             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18644         }
18645         if(!this.editable){
18646             this.editable = true;
18647             this.setEditable(false);
18648         }  
18649         
18650         
18651         if (typeof(this.events.add.listeners) != 'undefined') {
18652             
18653             this.addicon = this.wrap.createChild(
18654                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
18655        
18656             this.addicon.on('click', function(e) {
18657                 this.fireEvent('add', this);
18658             }, this);
18659         }
18660         if (typeof(this.events.edit.listeners) != 'undefined') {
18661             
18662             this.editicon = this.wrap.createChild(
18663                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
18664             if (this.addicon) {
18665                 this.editicon.setStyle('margin-left', '40px');
18666             }
18667             this.editicon.on('click', function(e) {
18668                 
18669                 // we fire even  if inothing is selected..
18670                 this.fireEvent('edit', this, this.lastData );
18671                 
18672             }, this);
18673         }
18674         
18675         
18676         
18677     },
18678
18679     // private
18680     initEvents : function(){
18681         Roo.form.ComboBox.superclass.initEvents.call(this);
18682
18683         this.keyNav = new Roo.KeyNav(this.el, {
18684             "up" : function(e){
18685                 this.inKeyMode = true;
18686                 this.selectPrev();
18687             },
18688
18689             "down" : function(e){
18690                 if(!this.isExpanded()){
18691                     this.onTriggerClick();
18692                 }else{
18693                     this.inKeyMode = true;
18694                     this.selectNext();
18695                 }
18696             },
18697
18698             "enter" : function(e){
18699                 this.onViewClick();
18700                 //return true;
18701             },
18702
18703             "esc" : function(e){
18704                 this.collapse();
18705             },
18706
18707             "tab" : function(e){
18708                 this.onViewClick(false);
18709                 this.fireEvent("specialkey", this, e);
18710                 return true;
18711             },
18712
18713             scope : this,
18714
18715             doRelay : function(foo, bar, hname){
18716                 if(hname == 'down' || this.scope.isExpanded()){
18717                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18718                 }
18719                 return true;
18720             },
18721
18722             forceKeyDown: true
18723         });
18724         this.queryDelay = Math.max(this.queryDelay || 10,
18725                 this.mode == 'local' ? 10 : 250);
18726         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18727         if(this.typeAhead){
18728             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18729         }
18730         if(this.editable !== false){
18731             this.el.on("keyup", this.onKeyUp, this);
18732         }
18733         if(this.forceSelection){
18734             this.on('blur', this.doForce, this);
18735         }
18736     },
18737
18738     onDestroy : function(){
18739         if(this.view){
18740             this.view.setStore(null);
18741             this.view.el.removeAllListeners();
18742             this.view.el.remove();
18743             this.view.purgeListeners();
18744         }
18745         if(this.list){
18746             this.list.destroy();
18747         }
18748         if(this.store){
18749             this.store.un('beforeload', this.onBeforeLoad, this);
18750             this.store.un('load', this.onLoad, this);
18751             this.store.un('loadexception', this.onLoadException, this);
18752         }
18753         Roo.form.ComboBox.superclass.onDestroy.call(this);
18754     },
18755
18756     // private
18757     fireKey : function(e){
18758         if(e.isNavKeyPress() && !this.list.isVisible()){
18759             this.fireEvent("specialkey", this, e);
18760         }
18761     },
18762
18763     // private
18764     onResize: function(w, h){
18765         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
18766         
18767         if(typeof w != 'number'){
18768             // we do not handle it!?!?
18769             return;
18770         }
18771         var tw = this.trigger.getWidth();
18772         tw += this.addicon ? this.addicon.getWidth() : 0;
18773         tw += this.editicon ? this.editicon.getWidth() : 0;
18774         var x = w - tw;
18775         this.el.setWidth( this.adjustWidth('input', x));
18776             
18777         this.trigger.setStyle('left', x+'px');
18778         
18779         if(this.list && this.listWidth === undefined){
18780             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
18781             this.list.setWidth(lw);
18782             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18783         }
18784         
18785     
18786         
18787     },
18788
18789     /**
18790      * Allow or prevent the user from directly editing the field text.  If false is passed,
18791      * the user will only be able to select from the items defined in the dropdown list.  This method
18792      * is the runtime equivalent of setting the 'editable' config option at config time.
18793      * @param {Boolean} value True to allow the user to directly edit the field text
18794      */
18795     setEditable : function(value){
18796         if(value == this.editable){
18797             return;
18798         }
18799         this.editable = value;
18800         if(!value){
18801             this.el.dom.setAttribute('readOnly', true);
18802             this.el.on('mousedown', this.onTriggerClick,  this);
18803             this.el.addClass('x-combo-noedit');
18804         }else{
18805             this.el.dom.setAttribute('readOnly', false);
18806             this.el.un('mousedown', this.onTriggerClick,  this);
18807             this.el.removeClass('x-combo-noedit');
18808         }
18809     },
18810
18811     // private
18812     onBeforeLoad : function(){
18813         if(!this.hasFocus){
18814             return;
18815         }
18816         this.innerList.update(this.loadingText ?
18817                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
18818         this.restrictHeight();
18819         this.selectedIndex = -1;
18820     },
18821
18822     // private
18823     onLoad : function(){
18824         if(!this.hasFocus){
18825             return;
18826         }
18827         if(this.store.getCount() > 0){
18828             this.expand();
18829             this.restrictHeight();
18830             if(this.lastQuery == this.allQuery){
18831                 if(this.editable){
18832                     this.el.dom.select();
18833                 }
18834                 if(!this.selectByValue(this.value, true)){
18835                     this.select(0, true);
18836                 }
18837             }else{
18838                 this.selectNext();
18839                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18840                     this.taTask.delay(this.typeAheadDelay);
18841                 }
18842             }
18843         }else{
18844             this.onEmptyResults();
18845         }
18846         //this.el.focus();
18847     },
18848     // private
18849     onLoadException : function()
18850     {
18851         this.collapse();
18852         Roo.log(this.store.reader.jsonData);
18853         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18854             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18855         }
18856         
18857         
18858     },
18859     // private
18860     onTypeAhead : function(){
18861         if(this.store.getCount() > 0){
18862             var r = this.store.getAt(0);
18863             var newValue = r.data[this.displayField];
18864             var len = newValue.length;
18865             var selStart = this.getRawValue().length;
18866             if(selStart != len){
18867                 this.setRawValue(newValue);
18868                 this.selectText(selStart, newValue.length);
18869             }
18870         }
18871     },
18872
18873     // private
18874     onSelect : function(record, index){
18875         if(this.fireEvent('beforeselect', this, record, index) !== false){
18876             this.setFromData(index > -1 ? record.data : false);
18877             this.collapse();
18878             this.fireEvent('select', this, record, index);
18879         }
18880     },
18881
18882     /**
18883      * Returns the currently selected field value or empty string if no value is set.
18884      * @return {String} value The selected value
18885      */
18886     getValue : function(){
18887         if(this.valueField){
18888             return typeof this.value != 'undefined' ? this.value : '';
18889         }
18890         return Roo.form.ComboBox.superclass.getValue.call(this);
18891     },
18892
18893     /**
18894      * Clears any text/value currently set in the field
18895      */
18896     clearValue : function(){
18897         if(this.hiddenField){
18898             this.hiddenField.value = '';
18899         }
18900         this.value = '';
18901         this.setRawValue('');
18902         this.lastSelectionText = '';
18903         
18904     },
18905
18906     /**
18907      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18908      * will be displayed in the field.  If the value does not match the data value of an existing item,
18909      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18910      * Otherwise the field will be blank (although the value will still be set).
18911      * @param {String} value The value to match
18912      */
18913     setValue : function(v){
18914         var text = v;
18915         if(this.valueField){
18916             var r = this.findRecord(this.valueField, v);
18917             if(r){
18918                 text = r.data[this.displayField];
18919             }else if(this.valueNotFoundText !== undefined){
18920                 text = this.valueNotFoundText;
18921             }
18922         }
18923         this.lastSelectionText = text;
18924         if(this.hiddenField){
18925             this.hiddenField.value = v;
18926         }
18927         Roo.form.ComboBox.superclass.setValue.call(this, text);
18928         this.value = v;
18929     },
18930     /**
18931      * @property {Object} the last set data for the element
18932      */
18933     
18934     lastData : false,
18935     /**
18936      * Sets the value of the field based on a object which is related to the record format for the store.
18937      * @param {Object} value the value to set as. or false on reset?
18938      */
18939     setFromData : function(o){
18940         var dv = ''; // display value
18941         var vv = ''; // value value..
18942         this.lastData = o;
18943         if (this.displayField) {
18944             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18945         } else {
18946             // this is an error condition!!!
18947             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18948         }
18949         
18950         if(this.valueField){
18951             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18952         }
18953         if(this.hiddenField){
18954             this.hiddenField.value = vv;
18955             
18956             this.lastSelectionText = dv;
18957             Roo.form.ComboBox.superclass.setValue.call(this, dv);
18958             this.value = vv;
18959             return;
18960         }
18961         // no hidden field.. - we store the value in 'value', but still display
18962         // display field!!!!
18963         this.lastSelectionText = dv;
18964         Roo.form.ComboBox.superclass.setValue.call(this, dv);
18965         this.value = vv;
18966         
18967         
18968     },
18969     // private
18970     reset : function(){
18971         // overridden so that last data is reset..
18972         this.setValue(this.resetValue);
18973         this.originalValue = this.getValue();
18974         this.clearInvalid();
18975         this.lastData = false;
18976         if (this.view) {
18977             this.view.clearSelections();
18978         }
18979     },
18980     // private
18981     findRecord : function(prop, value){
18982         var record;
18983         if(this.store.getCount() > 0){
18984             this.store.each(function(r){
18985                 if(r.data[prop] == value){
18986                     record = r;
18987                     return false;
18988                 }
18989                 return true;
18990             });
18991         }
18992         return record;
18993     },
18994     
18995     getName: function()
18996     {
18997         // returns hidden if it's set..
18998         if (!this.rendered) {return ''};
18999         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
19000         
19001     },
19002     // private
19003     onViewMove : function(e, t){
19004         this.inKeyMode = false;
19005     },
19006
19007     // private
19008     onViewOver : function(e, t){
19009         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19010             return;
19011         }
19012         var item = this.view.findItemFromChild(t);
19013         if(item){
19014             var index = this.view.indexOf(item);
19015             this.select(index, false);
19016         }
19017     },
19018
19019     // private
19020     onViewClick : function(doFocus)
19021     {
19022         var index = this.view.getSelectedIndexes()[0];
19023         var r = this.store.getAt(index);
19024         if(r){
19025             this.onSelect(r, index);
19026         }
19027         if(doFocus !== false && !this.blockFocus){
19028             this.el.focus();
19029         }
19030     },
19031
19032     // private
19033     restrictHeight : function(){
19034         this.innerList.dom.style.height = '';
19035         var inner = this.innerList.dom;
19036         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19037         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19038         this.list.beginUpdate();
19039         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19040         this.list.alignTo(this.el, this.listAlign);
19041         this.list.endUpdate();
19042     },
19043
19044     // private
19045     onEmptyResults : function(){
19046         this.collapse();
19047     },
19048
19049     /**
19050      * Returns true if the dropdown list is expanded, else false.
19051      */
19052     isExpanded : function(){
19053         return this.list.isVisible();
19054     },
19055
19056     /**
19057      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19058      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19059      * @param {String} value The data value of the item to select
19060      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19061      * selected item if it is not currently in view (defaults to true)
19062      * @return {Boolean} True if the value matched an item in the list, else false
19063      */
19064     selectByValue : function(v, scrollIntoView){
19065         if(v !== undefined && v !== null){
19066             var r = this.findRecord(this.valueField || this.displayField, v);
19067             if(r){
19068                 this.select(this.store.indexOf(r), scrollIntoView);
19069                 return true;
19070             }
19071         }
19072         return false;
19073     },
19074
19075     /**
19076      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19077      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19078      * @param {Number} index The zero-based index of the list item to select
19079      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19080      * selected item if it is not currently in view (defaults to true)
19081      */
19082     select : function(index, scrollIntoView){
19083         this.selectedIndex = index;
19084         this.view.select(index);
19085         if(scrollIntoView !== false){
19086             var el = this.view.getNode(index);
19087             if(el){
19088                 this.innerList.scrollChildIntoView(el, false);
19089             }
19090         }
19091     },
19092
19093     // private
19094     selectNext : function(){
19095         var ct = this.store.getCount();
19096         if(ct > 0){
19097             if(this.selectedIndex == -1){
19098                 this.select(0);
19099             }else if(this.selectedIndex < ct-1){
19100                 this.select(this.selectedIndex+1);
19101             }
19102         }
19103     },
19104
19105     // private
19106     selectPrev : function(){
19107         var ct = this.store.getCount();
19108         if(ct > 0){
19109             if(this.selectedIndex == -1){
19110                 this.select(0);
19111             }else if(this.selectedIndex != 0){
19112                 this.select(this.selectedIndex-1);
19113             }
19114         }
19115     },
19116
19117     // private
19118     onKeyUp : function(e){
19119         if(this.editable !== false && !e.isSpecialKey()){
19120             this.lastKey = e.getKey();
19121             this.dqTask.delay(this.queryDelay);
19122         }
19123     },
19124
19125     // private
19126     validateBlur : function(){
19127         return !this.list || !this.list.isVisible();   
19128     },
19129
19130     // private
19131     initQuery : function(){
19132         this.doQuery(this.getRawValue());
19133     },
19134
19135     // private
19136     doForce : function(){
19137         if(this.el.dom.value.length > 0){
19138             this.el.dom.value =
19139                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19140              
19141         }
19142     },
19143
19144     /**
19145      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19146      * query allowing the query action to be canceled if needed.
19147      * @param {String} query The SQL query to execute
19148      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19149      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19150      * saved in the current store (defaults to false)
19151      */
19152     doQuery : function(q, forceAll){
19153         if(q === undefined || q === null){
19154             q = '';
19155         }
19156         var qe = {
19157             query: q,
19158             forceAll: forceAll,
19159             combo: this,
19160             cancel:false
19161         };
19162         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19163             return false;
19164         }
19165         q = qe.query;
19166         forceAll = qe.forceAll;
19167         if(forceAll === true || (q.length >= this.minChars)){
19168             if(this.lastQuery != q || this.alwaysQuery){
19169                 this.lastQuery = q;
19170                 if(this.mode == 'local'){
19171                     this.selectedIndex = -1;
19172                     if(forceAll){
19173                         this.store.clearFilter();
19174                     }else{
19175                         this.store.filter(this.displayField, q);
19176                     }
19177                     this.onLoad();
19178                 }else{
19179                     this.store.baseParams[this.queryParam] = q;
19180                     this.store.load({
19181                         params: this.getParams(q)
19182                     });
19183                     this.expand();
19184                 }
19185             }else{
19186                 this.selectedIndex = -1;
19187                 this.onLoad();   
19188             }
19189         }
19190     },
19191
19192     // private
19193     getParams : function(q){
19194         var p = {};
19195         //p[this.queryParam] = q;
19196         if(this.pageSize){
19197             p.start = 0;
19198             p.limit = this.pageSize;
19199         }
19200         return p;
19201     },
19202
19203     /**
19204      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19205      */
19206     collapse : function(){
19207         if(!this.isExpanded()){
19208             return;
19209         }
19210         this.list.hide();
19211         Roo.get(document).un('mousedown', this.collapseIf, this);
19212         Roo.get(document).un('mousewheel', this.collapseIf, this);
19213         if (!this.editable) {
19214             Roo.get(document).un('keydown', this.listKeyPress, this);
19215         }
19216         this.fireEvent('collapse', this);
19217     },
19218
19219     // private
19220     collapseIf : function(e){
19221         if(!e.within(this.wrap) && !e.within(this.list)){
19222             this.collapse();
19223         }
19224     },
19225
19226     /**
19227      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19228      */
19229     expand : function(){
19230         if(this.isExpanded() || !this.hasFocus){
19231             return;
19232         }
19233         this.list.alignTo(this.el, this.listAlign);
19234         this.list.show();
19235         Roo.get(document).on('mousedown', this.collapseIf, this);
19236         Roo.get(document).on('mousewheel', this.collapseIf, this);
19237         if (!this.editable) {
19238             Roo.get(document).on('keydown', this.listKeyPress, this);
19239         }
19240         
19241         this.fireEvent('expand', this);
19242     },
19243
19244     // private
19245     // Implements the default empty TriggerField.onTriggerClick function
19246     onTriggerClick : function(){
19247         if(this.disabled){
19248             return;
19249         }
19250         if(this.isExpanded()){
19251             this.collapse();
19252             if (!this.blockFocus) {
19253                 this.el.focus();
19254             }
19255             
19256         }else {
19257             this.hasFocus = true;
19258             if(this.triggerAction == 'all') {
19259                 this.doQuery(this.allQuery, true);
19260             } else {
19261                 this.doQuery(this.getRawValue());
19262             }
19263             if (!this.blockFocus) {
19264                 this.el.focus();
19265             }
19266         }
19267     },
19268     listKeyPress : function(e)
19269     {
19270         //Roo.log('listkeypress');
19271         // scroll to first matching element based on key pres..
19272         if (e.isSpecialKey()) {
19273             return false;
19274         }
19275         var k = String.fromCharCode(e.getKey()).toUpperCase();
19276         //Roo.log(k);
19277         var match  = false;
19278         var csel = this.view.getSelectedNodes();
19279         var cselitem = false;
19280         if (csel.length) {
19281             var ix = this.view.indexOf(csel[0]);
19282             cselitem  = this.store.getAt(ix);
19283             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19284                 cselitem = false;
19285             }
19286             
19287         }
19288         
19289         this.store.each(function(v) { 
19290             if (cselitem) {
19291                 // start at existing selection.
19292                 if (cselitem.id == v.id) {
19293                     cselitem = false;
19294                 }
19295                 return;
19296             }
19297                 
19298             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19299                 match = this.store.indexOf(v);
19300                 return false;
19301             }
19302         }, this);
19303         
19304         if (match === false) {
19305             return true; // no more action?
19306         }
19307         // scroll to?
19308         this.view.select(match);
19309         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19310         sn.scrollIntoView(sn.dom.parentNode, false);
19311     } 
19312
19313     /** 
19314     * @cfg {Boolean} grow 
19315     * @hide 
19316     */
19317     /** 
19318     * @cfg {Number} growMin 
19319     * @hide 
19320     */
19321     /** 
19322     * @cfg {Number} growMax 
19323     * @hide 
19324     */
19325     /**
19326      * @hide
19327      * @method autoSize
19328      */
19329 });/*
19330  * Copyright(c) 2010-2012, Roo J Solutions Limited
19331  *
19332  * Licence LGPL
19333  *
19334  */
19335
19336 /**
19337  * @class Roo.form.ComboBoxArray
19338  * @extends Roo.form.TextField
19339  * A facebook style adder... for lists of email / people / countries  etc...
19340  * pick multiple items from a combo box, and shows each one.
19341  *
19342  *  Fred [x]  Brian [x]  [Pick another |v]
19343  *
19344  *
19345  *  For this to work: it needs various extra information
19346  *    - normal combo problay has
19347  *      name, hiddenName
19348  *    + displayField, valueField
19349  *
19350  *    For our purpose...
19351  *
19352  *
19353  *   If we change from 'extends' to wrapping...
19354  *   
19355  *  
19356  *
19357  
19358  
19359  * @constructor
19360  * Create a new ComboBoxArray.
19361  * @param {Object} config Configuration options
19362  */
19363  
19364
19365 Roo.form.ComboBoxArray = function(config)
19366 {
19367     this.addEvents({
19368         /**
19369          * @event beforeremove
19370          * Fires before remove the value from the list
19371              * @param {Roo.form.ComboBoxArray} _self This combo box array
19372              * @param {Roo.form.ComboBoxArray.Item} item removed item
19373              */
19374         'beforeremove' : true,
19375         /**
19376          * @event remove
19377          * Fires when remove the value from the list
19378              * @param {Roo.form.ComboBoxArray} _self This combo box array
19379              * @param {Roo.form.ComboBoxArray.Item} item removed item
19380              */
19381         'remove' : true
19382         
19383         
19384     });
19385     
19386     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19387     
19388     this.items = new Roo.util.MixedCollection(false);
19389     
19390     // construct the child combo...
19391     
19392     
19393     
19394     
19395    
19396     
19397 }
19398
19399  
19400 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19401
19402     /**
19403      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
19404      */
19405     
19406     lastData : false,
19407     
19408     // behavies liek a hiddne field
19409     inputType:      'hidden',
19410     /**
19411      * @cfg {Number} width The width of the box that displays the selected element
19412      */ 
19413     width:          300,
19414
19415     
19416     
19417     /**
19418      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19419      */
19420     name : false,
19421     /**
19422      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19423      */
19424     hiddenName : false,
19425       /**
19426      * @cfg {String} seperator    The value seperator normally ',' 
19427      */
19428     seperator : ',',
19429     
19430     // private the array of items that are displayed..
19431     items  : false,
19432     // private - the hidden field el.
19433     hiddenEl : false,
19434     // private - the filed el..
19435     el : false,
19436     
19437     //validateValue : function() { return true; }, // all values are ok!
19438     //onAddClick: function() { },
19439     
19440     onRender : function(ct, position) 
19441     {
19442         
19443         // create the standard hidden element
19444         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19445         
19446         
19447         // give fake names to child combo;
19448         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19449         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19450         
19451         this.combo = Roo.factory(this.combo, Roo.form);
19452         this.combo.onRender(ct, position);
19453         if (typeof(this.combo.width) != 'undefined') {
19454             this.combo.onResize(this.combo.width,0);
19455         }
19456         
19457         this.combo.initEvents();
19458         
19459         // assigned so form know we need to do this..
19460         this.store          = this.combo.store;
19461         this.valueField     = this.combo.valueField;
19462         this.displayField   = this.combo.displayField ;
19463         
19464         
19465         this.combo.wrap.addClass('x-cbarray-grp');
19466         
19467         var cbwrap = this.combo.wrap.createChild(
19468             {tag: 'div', cls: 'x-cbarray-cb'},
19469             this.combo.el.dom
19470         );
19471         
19472              
19473         this.hiddenEl = this.combo.wrap.createChild({
19474             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19475         });
19476         this.el = this.combo.wrap.createChild({
19477             tag: 'input',  type:'hidden' , name: this.name, value : ''
19478         });
19479          //   this.el.dom.removeAttribute("name");
19480         
19481         
19482         this.outerWrap = this.combo.wrap;
19483         this.wrap = cbwrap;
19484         
19485         this.outerWrap.setWidth(this.width);
19486         this.outerWrap.dom.removeChild(this.el.dom);
19487         
19488         this.wrap.dom.appendChild(this.el.dom);
19489         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19490         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19491         
19492         this.combo.trigger.setStyle('position','relative');
19493         this.combo.trigger.setStyle('left', '0px');
19494         this.combo.trigger.setStyle('top', '2px');
19495         
19496         this.combo.el.setStyle('vertical-align', 'text-bottom');
19497         
19498         //this.trigger.setStyle('vertical-align', 'top');
19499         
19500         // this should use the code from combo really... on('add' ....)
19501         if (this.adder) {
19502             
19503         
19504             this.adder = this.outerWrap.createChild(
19505                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19506             var _t = this;
19507             this.adder.on('click', function(e) {
19508                 _t.fireEvent('adderclick', this, e);
19509             }, _t);
19510         }
19511         //var _t = this;
19512         //this.adder.on('click', this.onAddClick, _t);
19513         
19514         
19515         this.combo.on('select', function(cb, rec, ix) {
19516             this.addItem(rec.data);
19517             
19518             cb.setValue('');
19519             cb.el.dom.value = '';
19520             //cb.lastData = rec.data;
19521             // add to list
19522             
19523         }, this);
19524         
19525         
19526     },
19527     
19528     
19529     getName: function()
19530     {
19531         // returns hidden if it's set..
19532         if (!this.rendered) {return ''};
19533         return  this.hiddenName ? this.hiddenName : this.name;
19534         
19535     },
19536     
19537     
19538     onResize: function(w, h){
19539         
19540         return;
19541         // not sure if this is needed..
19542         //this.combo.onResize(w,h);
19543         
19544         if(typeof w != 'number'){
19545             // we do not handle it!?!?
19546             return;
19547         }
19548         var tw = this.combo.trigger.getWidth();
19549         tw += this.addicon ? this.addicon.getWidth() : 0;
19550         tw += this.editicon ? this.editicon.getWidth() : 0;
19551         var x = w - tw;
19552         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19553             
19554         this.combo.trigger.setStyle('left', '0px');
19555         
19556         if(this.list && this.listWidth === undefined){
19557             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19558             this.list.setWidth(lw);
19559             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19560         }
19561         
19562     
19563         
19564     },
19565     
19566     addItem: function(rec)
19567     {
19568         var valueField = this.combo.valueField;
19569         var displayField = this.combo.displayField;
19570         
19571         if (this.items.indexOfKey(rec[valueField]) > -1) {
19572             //console.log("GOT " + rec.data.id);
19573             return;
19574         }
19575         
19576         var x = new Roo.form.ComboBoxArray.Item({
19577             //id : rec[this.idField],
19578             data : rec,
19579             displayField : displayField ,
19580             tipField : displayField ,
19581             cb : this
19582         });
19583         // use the 
19584         this.items.add(rec[valueField],x);
19585         // add it before the element..
19586         this.updateHiddenEl();
19587         x.render(this.outerWrap, this.wrap.dom);
19588         // add the image handler..
19589     },
19590     
19591     updateHiddenEl : function()
19592     {
19593         this.validate();
19594         if (!this.hiddenEl) {
19595             return;
19596         }
19597         var ar = [];
19598         var idField = this.combo.valueField;
19599         
19600         this.items.each(function(f) {
19601             ar.push(f.data[idField]);
19602         });
19603         this.hiddenEl.dom.value = ar.join(this.seperator);
19604         this.validate();
19605     },
19606     
19607     reset : function()
19608     {
19609         this.items.clear();
19610         
19611         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19612            el.remove();
19613         });
19614         
19615         this.el.dom.value = '';
19616         if (this.hiddenEl) {
19617             this.hiddenEl.dom.value = '';
19618         }
19619         
19620     },
19621     getValue: function()
19622     {
19623         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19624     },
19625     setValue: function(v) // not a valid action - must use addItems..
19626     {
19627         
19628         this.reset();
19629          
19630         if (this.store.isLocal && (typeof(v) == 'string')) {
19631             // then we can use the store to find the values..
19632             // comma seperated at present.. this needs to allow JSON based encoding..
19633             this.hiddenEl.value  = v;
19634             var v_ar = [];
19635             Roo.each(v.split(this.seperator), function(k) {
19636                 Roo.log("CHECK " + this.valueField + ',' + k);
19637                 var li = this.store.query(this.valueField, k);
19638                 if (!li.length) {
19639                     return;
19640                 }
19641                 var add = {};
19642                 add[this.valueField] = k;
19643                 add[this.displayField] = li.item(0).data[this.displayField];
19644                 
19645                 this.addItem(add);
19646             }, this) 
19647              
19648         }
19649         if (typeof(v) == 'object' ) {
19650             // then let's assume it's an array of objects..
19651             Roo.each(v, function(l) {
19652                 var add = l;
19653                 if (typeof(l) == 'string') {
19654                     add = {};
19655                     add[this.valueField] = l;
19656                     add[this.displayField] = l
19657                 }
19658                 this.addItem(add);
19659             }, this);
19660              
19661         }
19662         
19663         
19664     },
19665     setFromData: function(v)
19666     {
19667         // this recieves an object, if setValues is called.
19668         this.reset();
19669         this.el.dom.value = v[this.displayField];
19670         this.hiddenEl.dom.value = v[this.valueField];
19671         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19672             return;
19673         }
19674         var kv = v[this.valueField];
19675         var dv = v[this.displayField];
19676         kv = typeof(kv) != 'string' ? '' : kv;
19677         dv = typeof(dv) != 'string' ? '' : dv;
19678         
19679         
19680         var keys = kv.split(this.seperator);
19681         var display = dv.split(this.seperator);
19682         for (var i = 0 ; i < keys.length; i++) {
19683             add = {};
19684             add[this.valueField] = keys[i];
19685             add[this.displayField] = display[i];
19686             this.addItem(add);
19687         }
19688       
19689         
19690     },
19691     
19692     /**
19693      * Validates the combox array value
19694      * @return {Boolean} True if the value is valid, else false
19695      */
19696     validate : function(){
19697         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19698             this.clearInvalid();
19699             return true;
19700         }
19701         return false;
19702     },
19703     
19704     validateValue : function(value){
19705         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19706         
19707     },
19708     
19709     /*@
19710      * overide
19711      * 
19712      */
19713     isDirty : function() {
19714         if(this.disabled) {
19715             return false;
19716         }
19717         
19718         try {
19719             var d = Roo.decode(String(this.originalValue));
19720         } catch (e) {
19721             return String(this.getValue()) !== String(this.originalValue);
19722         }
19723         
19724         var originalValue = [];
19725         
19726         for (var i = 0; i < d.length; i++){
19727             originalValue.push(d[i][this.valueField]);
19728         }
19729         
19730         return String(this.getValue()) !== String(originalValue.join(this.seperator));
19731         
19732     }
19733     
19734 });
19735
19736
19737
19738 /**
19739  * @class Roo.form.ComboBoxArray.Item
19740  * @extends Roo.BoxComponent
19741  * A selected item in the list
19742  *  Fred [x]  Brian [x]  [Pick another |v]
19743  * 
19744  * @constructor
19745  * Create a new item.
19746  * @param {Object} config Configuration options
19747  */
19748  
19749 Roo.form.ComboBoxArray.Item = function(config) {
19750     config.id = Roo.id();
19751     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
19752 }
19753
19754 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
19755     data : {},
19756     cb: false,
19757     displayField : false,
19758     tipField : false,
19759     
19760     
19761     defaultAutoCreate : {
19762         tag: 'div',
19763         cls: 'x-cbarray-item',
19764         cn : [ 
19765             { tag: 'div' },
19766             {
19767                 tag: 'img',
19768                 width:16,
19769                 height : 16,
19770                 src : Roo.BLANK_IMAGE_URL ,
19771                 align: 'center'
19772             }
19773         ]
19774         
19775     },
19776     
19777  
19778     onRender : function(ct, position)
19779     {
19780         Roo.form.Field.superclass.onRender.call(this, ct, position);
19781         
19782         if(!this.el){
19783             var cfg = this.getAutoCreate();
19784             this.el = ct.createChild(cfg, position);
19785         }
19786         
19787         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
19788         
19789         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
19790             this.cb.renderer(this.data) :
19791             String.format('{0}',this.data[this.displayField]);
19792         
19793             
19794         this.el.child('div').dom.setAttribute('qtip',
19795                         String.format('{0}',this.data[this.tipField])
19796         );
19797         
19798         this.el.child('img').on('click', this.remove, this);
19799         
19800     },
19801    
19802     remove : function()
19803     {
19804         if(this.cb.disabled){
19805             return;
19806         }
19807         
19808         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
19809             this.cb.items.remove(this);
19810             this.el.child('img').un('click', this.remove, this);
19811             this.el.remove();
19812             this.cb.updateHiddenEl();
19813
19814             this.cb.fireEvent('remove', this.cb, this);
19815         }
19816         
19817     }
19818 });/*
19819  * RooJS Library 1.1.1
19820  * Copyright(c) 2008-2011  Alan Knowles
19821  *
19822  * License - LGPL
19823  */
19824  
19825
19826 /**
19827  * @class Roo.form.ComboNested
19828  * @extends Roo.form.ComboBox
19829  * A combobox for that allows selection of nested items in a list,
19830  * eg.
19831  *
19832  *  Book
19833  *    -> red
19834  *    -> green
19835  *  Table
19836  *    -> square
19837  *      ->red
19838  *      ->green
19839  *    -> rectangle
19840  *      ->green
19841  *      
19842  * 
19843  * @constructor
19844  * Create a new ComboNested
19845  * @param {Object} config Configuration options
19846  */
19847 Roo.form.ComboNested = function(config){
19848     Roo.form.ComboCheck.superclass.constructor.call(this, config);
19849     // should verify some data...
19850     // like
19851     // hiddenName = required..
19852     // displayField = required
19853     // valudField == required
19854     var req= [ 'hiddenName', 'displayField', 'valueField' ];
19855     var _t = this;
19856     Roo.each(req, function(e) {
19857         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
19858             throw "Roo.form.ComboNested : missing value for: " + e;
19859         }
19860     });
19861      
19862     
19863 };
19864
19865 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
19866    
19867     /*
19868      * @config {Number} max Number of columns to show
19869      */
19870     
19871     maxColumns : 3,
19872    
19873     list : null, // the outermost div..
19874     innerLists : null, // the
19875     views : null,
19876     stores : null,
19877     // private
19878     loadingChildren : false,
19879     
19880     onRender : function(ct, position)
19881     {
19882         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
19883         
19884         if(this.hiddenName){
19885             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
19886                     'before', true);
19887             this.hiddenField.value =
19888                 this.hiddenValue !== undefined ? this.hiddenValue :
19889                 this.value !== undefined ? this.value : '';
19890
19891             // prevent input submission
19892             this.el.dom.removeAttribute('name');
19893              
19894              
19895         }
19896         
19897         if(Roo.isGecko){
19898             this.el.dom.setAttribute('autocomplete', 'off');
19899         }
19900
19901         var cls = 'x-combo-list';
19902
19903         this.list = new Roo.Layer({
19904             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
19905         });
19906
19907         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
19908         this.list.setWidth(lw);
19909         this.list.swallowEvent('mousewheel');
19910         this.assetHeight = 0;
19911
19912         if(this.title){
19913             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
19914             this.assetHeight += this.header.getHeight();
19915         }
19916         this.innerLists = [];
19917         this.views = [];
19918         this.stores = [];
19919         for (var i =0 ; i < this.maxColumns; i++) {
19920             this.onRenderList( cls, i);
19921         }
19922         
19923         // always needs footer, as we are going to have an 'OK' button.
19924         this.footer = this.list.createChild({cls:cls+'-ft'});
19925         this.pageTb = new Roo.Toolbar(this.footer);  
19926         var _this = this;
19927         this.pageTb.add(  {
19928             
19929             text: 'Done',
19930             handler: function()
19931             {
19932                 _this.collapse();
19933             }
19934         });
19935         
19936         if ( this.allowBlank && !this.disableClear) {
19937             
19938             this.pageTb.add(new Roo.Toolbar.Fill(), {
19939                 cls: 'x-btn-icon x-btn-clear',
19940                 text: '&#160;',
19941                 handler: function()
19942                 {
19943                     _this.collapse();
19944                     _this.clearValue();
19945                     _this.onSelect(false, -1);
19946                 }
19947             });
19948         }
19949         if (this.footer) {
19950             this.assetHeight += this.footer.getHeight();
19951         }
19952         
19953     },
19954     onRenderList : function (  cls, i)
19955     {
19956         
19957         var lw = Math.floor(
19958                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
19959         );
19960         
19961         this.list.setWidth(lw); // default to '1'
19962
19963         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
19964         //il.on('mouseover', this.onViewOver, this, { list:  i });
19965         //il.on('mousemove', this.onViewMove, this, { list:  i });
19966         il.setWidth(lw);
19967         il.setStyle({ 'overflow-x' : 'hidden'});
19968
19969         if(!this.tpl){
19970             this.tpl = new Roo.Template({
19971                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
19972                 isEmpty: function (value, allValues) {
19973                     //Roo.log(value);
19974                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
19975                     return dl ? 'has-children' : 'no-children'
19976                 }
19977             });
19978         }
19979         
19980         var store  = this.store;
19981         if (i > 0) {
19982             store  = new Roo.data.SimpleStore({
19983                 //fields : this.store.reader.meta.fields,
19984                 reader : this.store.reader,
19985                 data : [ ]
19986             });
19987         }
19988         this.stores[i]  = store;
19989                   
19990         var view = this.views[i] = new Roo.View(
19991             il,
19992             this.tpl,
19993             {
19994                 singleSelect:true,
19995                 store: store,
19996                 selectedClass: this.selectedClass
19997             }
19998         );
19999         view.getEl().setWidth(lw);
20000         view.getEl().setStyle({
20001             position: i < 1 ? 'relative' : 'absolute',
20002             top: 0,
20003             left: (i * lw ) + 'px',
20004             display : i > 0 ? 'none' : 'block'
20005         });
20006         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
20007         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
20008         //view.on('click', this.onViewClick, this, { list : i });
20009
20010         store.on('beforeload', this.onBeforeLoad, this);
20011         store.on('load',  this.onLoad, this, { list  : i});
20012         store.on('loadexception', this.onLoadException, this);
20013
20014         // hide the other vies..
20015         
20016         
20017         
20018     },
20019       
20020     restrictHeight : function()
20021     {
20022         var mh = 0;
20023         Roo.each(this.innerLists, function(il,i) {
20024             var el = this.views[i].getEl();
20025             el.dom.style.height = '';
20026             var inner = el.dom;
20027             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
20028             // only adjust heights on other ones..
20029             mh = Math.max(h, mh);
20030             if (i < 1) {
20031                 
20032                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20033                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20034                
20035             }
20036             
20037             
20038         }, this);
20039         
20040         this.list.beginUpdate();
20041         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20042         this.list.alignTo(this.el, this.listAlign);
20043         this.list.endUpdate();
20044         
20045     },
20046      
20047     
20048     // -- store handlers..
20049     // private
20050     onBeforeLoad : function()
20051     {
20052         if(!this.hasFocus){
20053             return;
20054         }
20055         this.innerLists[0].update(this.loadingText ?
20056                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20057         this.restrictHeight();
20058         this.selectedIndex = -1;
20059     },
20060     // private
20061     onLoad : function(a,b,c,d)
20062     {
20063         if (!this.loadingChildren) {
20064             // then we are loading the top level. - hide the children
20065             for (var i = 1;i < this.views.length; i++) {
20066                 this.views[i].getEl().setStyle({ display : 'none' });
20067             }
20068             var lw = Math.floor(
20069                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20070             );
20071         
20072              this.list.setWidth(lw); // default to '1'
20073
20074             
20075         }
20076         if(!this.hasFocus){
20077             return;
20078         }
20079         
20080         if(this.store.getCount() > 0) {
20081             this.expand();
20082             this.restrictHeight();   
20083         } else {
20084             this.onEmptyResults();
20085         }
20086         
20087         if (!this.loadingChildren) {
20088             this.selectActive();
20089         }
20090         /*
20091         this.stores[1].loadData([]);
20092         this.stores[2].loadData([]);
20093         this.views
20094         */    
20095     
20096         //this.el.focus();
20097     },
20098     
20099     
20100     // private
20101     onLoadException : function()
20102     {
20103         this.collapse();
20104         Roo.log(this.store.reader.jsonData);
20105         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20106             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20107         }
20108         
20109         
20110     },
20111     // no cleaning of leading spaces on blur here.
20112     cleanLeadingSpace : function(e) { },
20113     
20114
20115     onSelectChange : function (view, sels, opts )
20116     {
20117         var ix = view.getSelectedIndexes();
20118          
20119         if (opts.list > this.maxColumns - 2) {
20120             if (view.store.getCount()<  1) {
20121                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
20122
20123             } else  {
20124                 if (ix.length) {
20125                     // used to clear ?? but if we are loading unselected 
20126                     this.setFromData(view.store.getAt(ix[0]).data);
20127                 }
20128                 
20129             }
20130             
20131             return;
20132         }
20133         
20134         if (!ix.length) {
20135             // this get's fired when trigger opens..
20136            // this.setFromData({});
20137             var str = this.stores[opts.list+1];
20138             str.data.clear(); // removeall wihtout the fire events..
20139             return;
20140         }
20141         
20142         var rec = view.store.getAt(ix[0]);
20143          
20144         this.setFromData(rec.data);
20145         this.fireEvent('select', this, rec, ix[0]);
20146         
20147         var lw = Math.floor(
20148              (
20149                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
20150              ) / this.maxColumns
20151         );
20152         this.loadingChildren = true;
20153         this.stores[opts.list+1].loadDataFromChildren( rec );
20154         this.loadingChildren = false;
20155         var dl = this.stores[opts.list+1]. getTotalCount();
20156         
20157         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20158         
20159         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20160         for (var i = opts.list+2; i < this.views.length;i++) {
20161             this.views[i].getEl().setStyle({ display : 'none' });
20162         }
20163         
20164         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20165         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
20166         
20167         if (this.isLoading) {
20168            // this.selectActive(opts.list);
20169         }
20170          
20171     },
20172     
20173     
20174     
20175     
20176     onDoubleClick : function()
20177     {
20178         this.collapse(); //??
20179     },
20180     
20181      
20182     
20183     
20184     
20185     // private
20186     recordToStack : function(store, prop, value, stack)
20187     {
20188         var cstore = new Roo.data.SimpleStore({
20189             //fields : this.store.reader.meta.fields, // we need array reader.. for
20190             reader : this.store.reader,
20191             data : [ ]
20192         });
20193         var _this = this;
20194         var record  = false;
20195         var srec = false;
20196         if(store.getCount() < 1){
20197             return false;
20198         }
20199         store.each(function(r){
20200             if(r.data[prop] == value){
20201                 record = r;
20202             srec = r;
20203                 return false;
20204             }
20205             if (r.data.cn && r.data.cn.length) {
20206                 cstore.loadDataFromChildren( r);
20207                 var cret = _this.recordToStack(cstore, prop, value, stack);
20208                 if (cret !== false) {
20209                     record = cret;
20210                     srec = r;
20211                     return false;
20212                 }
20213             }
20214              
20215             return true;
20216         });
20217         if (record == false) {
20218             return false
20219         }
20220         stack.unshift(srec);
20221         return record;
20222     },
20223     
20224     /*
20225      * find the stack of stores that match our value.
20226      *
20227      * 
20228      */
20229     
20230     selectActive : function ()
20231     {
20232         // if store is not loaded, then we will need to wait for that to happen first.
20233         var stack = [];
20234         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
20235         for (var i = 0; i < stack.length; i++ ) {
20236             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
20237         }
20238         
20239     }
20240         
20241          
20242     
20243     
20244     
20245     
20246 });/*
20247  * Based on:
20248  * Ext JS Library 1.1.1
20249  * Copyright(c) 2006-2007, Ext JS, LLC.
20250  *
20251  * Originally Released Under LGPL - original licence link has changed is not relivant.
20252  *
20253  * Fork - LGPL
20254  * <script type="text/javascript">
20255  */
20256 /**
20257  * @class Roo.form.Checkbox
20258  * @extends Roo.form.Field
20259  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20260  * @constructor
20261  * Creates a new Checkbox
20262  * @param {Object} config Configuration options
20263  */
20264 Roo.form.Checkbox = function(config){
20265     Roo.form.Checkbox.superclass.constructor.call(this, config);
20266     this.addEvents({
20267         /**
20268          * @event check
20269          * Fires when the checkbox is checked or unchecked.
20270              * @param {Roo.form.Checkbox} this This checkbox
20271              * @param {Boolean} checked The new checked value
20272              */
20273         check : true
20274     });
20275 };
20276
20277 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20278     /**
20279      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20280      */
20281     focusClass : undefined,
20282     /**
20283      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20284      */
20285     fieldClass: "x-form-field",
20286     /**
20287      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20288      */
20289     checked: false,
20290     /**
20291      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20292      * {tag: "input", type: "checkbox", autocomplete: "off"})
20293      */
20294     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20295     /**
20296      * @cfg {String} boxLabel The text that appears beside the checkbox
20297      */
20298     boxLabel : "",
20299     /**
20300      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20301      */  
20302     inputValue : '1',
20303     /**
20304      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20305      */
20306      valueOff: '0', // value when not checked..
20307
20308     actionMode : 'viewEl', 
20309     //
20310     // private
20311     itemCls : 'x-menu-check-item x-form-item',
20312     groupClass : 'x-menu-group-item',
20313     inputType : 'hidden',
20314     
20315     
20316     inSetChecked: false, // check that we are not calling self...
20317     
20318     inputElement: false, // real input element?
20319     basedOn: false, // ????
20320     
20321     isFormField: true, // not sure where this is needed!!!!
20322
20323     onResize : function(){
20324         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20325         if(!this.boxLabel){
20326             this.el.alignTo(this.wrap, 'c-c');
20327         }
20328     },
20329
20330     initEvents : function(){
20331         Roo.form.Checkbox.superclass.initEvents.call(this);
20332         this.el.on("click", this.onClick,  this);
20333         this.el.on("change", this.onClick,  this);
20334     },
20335
20336
20337     getResizeEl : function(){
20338         return this.wrap;
20339     },
20340
20341     getPositionEl : function(){
20342         return this.wrap;
20343     },
20344
20345     // private
20346     onRender : function(ct, position){
20347         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20348         /*
20349         if(this.inputValue !== undefined){
20350             this.el.dom.value = this.inputValue;
20351         }
20352         */
20353         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20354         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20355         var viewEl = this.wrap.createChild({ 
20356             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20357         this.viewEl = viewEl;   
20358         this.wrap.on('click', this.onClick,  this); 
20359         
20360         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20361         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20362         
20363         
20364         
20365         if(this.boxLabel){
20366             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20367         //    viewEl.on('click', this.onClick,  this); 
20368         }
20369         //if(this.checked){
20370             this.setChecked(this.checked);
20371         //}else{
20372             //this.checked = this.el.dom;
20373         //}
20374
20375     },
20376
20377     // private
20378     initValue : Roo.emptyFn,
20379
20380     /**
20381      * Returns the checked state of the checkbox.
20382      * @return {Boolean} True if checked, else false
20383      */
20384     getValue : function(){
20385         if(this.el){
20386             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20387         }
20388         return this.valueOff;
20389         
20390     },
20391
20392         // private
20393     onClick : function(){ 
20394         if (this.disabled) {
20395             return;
20396         }
20397         this.setChecked(!this.checked);
20398
20399         //if(this.el.dom.checked != this.checked){
20400         //    this.setValue(this.el.dom.checked);
20401        // }
20402     },
20403
20404     /**
20405      * Sets the checked state of the checkbox.
20406      * On is always based on a string comparison between inputValue and the param.
20407      * @param {Boolean/String} value - the value to set 
20408      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20409      */
20410     setValue : function(v,suppressEvent){
20411         
20412         
20413         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20414         //if(this.el && this.el.dom){
20415         //    this.el.dom.checked = this.checked;
20416         //    this.el.dom.defaultChecked = this.checked;
20417         //}
20418         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20419         //this.fireEvent("check", this, this.checked);
20420     },
20421     // private..
20422     setChecked : function(state,suppressEvent)
20423     {
20424         if (this.inSetChecked) {
20425             this.checked = state;
20426             return;
20427         }
20428         
20429     
20430         if(this.wrap){
20431             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20432         }
20433         this.checked = state;
20434         if(suppressEvent !== true){
20435             this.fireEvent('check', this, state);
20436         }
20437         this.inSetChecked = true;
20438         this.el.dom.value = state ? this.inputValue : this.valueOff;
20439         this.inSetChecked = false;
20440         
20441     },
20442     // handle setting of hidden value by some other method!!?!?
20443     setFromHidden: function()
20444     {
20445         if(!this.el){
20446             return;
20447         }
20448         //console.log("SET FROM HIDDEN");
20449         //alert('setFrom hidden');
20450         this.setValue(this.el.dom.value);
20451     },
20452     
20453     onDestroy : function()
20454     {
20455         if(this.viewEl){
20456             Roo.get(this.viewEl).remove();
20457         }
20458          
20459         Roo.form.Checkbox.superclass.onDestroy.call(this);
20460     },
20461     
20462     setBoxLabel : function(str)
20463     {
20464         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20465     }
20466
20467 });/*
20468  * Based on:
20469  * Ext JS Library 1.1.1
20470  * Copyright(c) 2006-2007, Ext JS, LLC.
20471  *
20472  * Originally Released Under LGPL - original licence link has changed is not relivant.
20473  *
20474  * Fork - LGPL
20475  * <script type="text/javascript">
20476  */
20477  
20478 /**
20479  * @class Roo.form.Radio
20480  * @extends Roo.form.Checkbox
20481  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20482  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20483  * @constructor
20484  * Creates a new Radio
20485  * @param {Object} config Configuration options
20486  */
20487 Roo.form.Radio = function(){
20488     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20489 };
20490 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20491     inputType: 'radio',
20492
20493     /**
20494      * If this radio is part of a group, it will return the selected value
20495      * @return {String}
20496      */
20497     getGroupValue : function(){
20498         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20499     },
20500     
20501     
20502     onRender : function(ct, position){
20503         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20504         
20505         if(this.inputValue !== undefined){
20506             this.el.dom.value = this.inputValue;
20507         }
20508          
20509         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20510         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20511         //var viewEl = this.wrap.createChild({ 
20512         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20513         //this.viewEl = viewEl;   
20514         //this.wrap.on('click', this.onClick,  this); 
20515         
20516         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20517         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20518         
20519         
20520         
20521         if(this.boxLabel){
20522             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20523         //    viewEl.on('click', this.onClick,  this); 
20524         }
20525          if(this.checked){
20526             this.el.dom.checked =   'checked' ;
20527         }
20528          
20529     } 
20530     
20531     
20532 });//<script type="text/javascript">
20533
20534 /*
20535  * Based  Ext JS Library 1.1.1
20536  * Copyright(c) 2006-2007, Ext JS, LLC.
20537  * LGPL
20538  *
20539  */
20540  
20541 /**
20542  * @class Roo.HtmlEditorCore
20543  * @extends Roo.Component
20544  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20545  *
20546  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20547  */
20548
20549 Roo.HtmlEditorCore = function(config){
20550     
20551     
20552     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20553     
20554     
20555     this.addEvents({
20556         /**
20557          * @event initialize
20558          * Fires when the editor is fully initialized (including the iframe)
20559          * @param {Roo.HtmlEditorCore} this
20560          */
20561         initialize: true,
20562         /**
20563          * @event activate
20564          * Fires when the editor is first receives the focus. Any insertion must wait
20565          * until after this event.
20566          * @param {Roo.HtmlEditorCore} this
20567          */
20568         activate: true,
20569          /**
20570          * @event beforesync
20571          * Fires before the textarea is updated with content from the editor iframe. Return false
20572          * to cancel the sync.
20573          * @param {Roo.HtmlEditorCore} this
20574          * @param {String} html
20575          */
20576         beforesync: true,
20577          /**
20578          * @event beforepush
20579          * Fires before the iframe editor is updated with content from the textarea. Return false
20580          * to cancel the push.
20581          * @param {Roo.HtmlEditorCore} this
20582          * @param {String} html
20583          */
20584         beforepush: true,
20585          /**
20586          * @event sync
20587          * Fires when the textarea is updated with content from the editor iframe.
20588          * @param {Roo.HtmlEditorCore} this
20589          * @param {String} html
20590          */
20591         sync: true,
20592          /**
20593          * @event push
20594          * Fires when the iframe editor is updated with content from the textarea.
20595          * @param {Roo.HtmlEditorCore} this
20596          * @param {String} html
20597          */
20598         push: true,
20599         
20600         /**
20601          * @event editorevent
20602          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20603          * @param {Roo.HtmlEditorCore} this
20604          */
20605         editorevent: true
20606         
20607     });
20608     
20609     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20610     
20611     // defaults : white / black...
20612     this.applyBlacklists();
20613     
20614     
20615     
20616 };
20617
20618
20619 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20620
20621
20622      /**
20623      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20624      */
20625     
20626     owner : false,
20627     
20628      /**
20629      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20630      *                        Roo.resizable.
20631      */
20632     resizable : false,
20633      /**
20634      * @cfg {Number} height (in pixels)
20635      */   
20636     height: 300,
20637    /**
20638      * @cfg {Number} width (in pixels)
20639      */   
20640     width: 500,
20641     
20642     /**
20643      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20644      * 
20645      */
20646     stylesheets: false,
20647     
20648     /**
20649      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
20650      */
20651     allowComments: false,
20652     // id of frame..
20653     frameId: false,
20654     
20655     // private properties
20656     validationEvent : false,
20657     deferHeight: true,
20658     initialized : false,
20659     activated : false,
20660     sourceEditMode : false,
20661     onFocus : Roo.emptyFn,
20662     iframePad:3,
20663     hideMode:'offsets',
20664     
20665     clearUp: true,
20666     
20667     // blacklist + whitelisted elements..
20668     black: false,
20669     white: false,
20670      
20671     bodyCls : '',
20672
20673     /**
20674      * Protected method that will not generally be called directly. It
20675      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20676      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20677      */
20678     getDocMarkup : function(){
20679         // body styles..
20680         var st = '';
20681         
20682         // inherit styels from page...?? 
20683         if (this.stylesheets === false) {
20684             
20685             Roo.get(document.head).select('style').each(function(node) {
20686                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20687             });
20688             
20689             Roo.get(document.head).select('link').each(function(node) { 
20690                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20691             });
20692             
20693         } else if (!this.stylesheets.length) {
20694                 // simple..
20695                 st = '<style type="text/css">' +
20696                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20697                    '</style>';
20698         } else {
20699             for (var i in this.stylesheets) { 
20700                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
20701             }
20702             
20703         }
20704         
20705         st +=  '<style type="text/css">' +
20706             'IMG { cursor: pointer } ' +
20707         '</style>';
20708
20709         var cls = 'roo-htmleditor-body';
20710         
20711         if(this.bodyCls.length){
20712             cls += ' ' + this.bodyCls;
20713         }
20714         
20715         return '<html><head>' + st  +
20716             //<style type="text/css">' +
20717             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20718             //'</style>' +
20719             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
20720     },
20721
20722     // private
20723     onRender : function(ct, position)
20724     {
20725         var _t = this;
20726         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20727         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20728         
20729         
20730         this.el.dom.style.border = '0 none';
20731         this.el.dom.setAttribute('tabIndex', -1);
20732         this.el.addClass('x-hidden hide');
20733         
20734         
20735         
20736         if(Roo.isIE){ // fix IE 1px bogus margin
20737             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20738         }
20739        
20740         
20741         this.frameId = Roo.id();
20742         
20743          
20744         
20745         var iframe = this.owner.wrap.createChild({
20746             tag: 'iframe',
20747             cls: 'form-control', // bootstrap..
20748             id: this.frameId,
20749             name: this.frameId,
20750             frameBorder : 'no',
20751             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20752         }, this.el
20753         );
20754         
20755         
20756         this.iframe = iframe.dom;
20757
20758          this.assignDocWin();
20759         
20760         this.doc.designMode = 'on';
20761        
20762         this.doc.open();
20763         this.doc.write(this.getDocMarkup());
20764         this.doc.close();
20765
20766         
20767         var task = { // must defer to wait for browser to be ready
20768             run : function(){
20769                 //console.log("run task?" + this.doc.readyState);
20770                 this.assignDocWin();
20771                 if(this.doc.body || this.doc.readyState == 'complete'){
20772                     try {
20773                         this.doc.designMode="on";
20774                     } catch (e) {
20775                         return;
20776                     }
20777                     Roo.TaskMgr.stop(task);
20778                     this.initEditor.defer(10, this);
20779                 }
20780             },
20781             interval : 10,
20782             duration: 10000,
20783             scope: this
20784         };
20785         Roo.TaskMgr.start(task);
20786
20787     },
20788
20789     // private
20790     onResize : function(w, h)
20791     {
20792          Roo.log('resize: ' +w + ',' + h );
20793         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20794         if(!this.iframe){
20795             return;
20796         }
20797         if(typeof w == 'number'){
20798             
20799             this.iframe.style.width = w + 'px';
20800         }
20801         if(typeof h == 'number'){
20802             
20803             this.iframe.style.height = h + 'px';
20804             if(this.doc){
20805                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20806             }
20807         }
20808         
20809     },
20810
20811     /**
20812      * Toggles the editor between standard and source edit mode.
20813      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20814      */
20815     toggleSourceEdit : function(sourceEditMode){
20816         
20817         this.sourceEditMode = sourceEditMode === true;
20818         
20819         if(this.sourceEditMode){
20820  
20821             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20822             
20823         }else{
20824             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20825             //this.iframe.className = '';
20826             this.deferFocus();
20827         }
20828         //this.setSize(this.owner.wrap.getSize());
20829         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20830     },
20831
20832     
20833   
20834
20835     /**
20836      * Protected method that will not generally be called directly. If you need/want
20837      * custom HTML cleanup, this is the method you should override.
20838      * @param {String} html The HTML to be cleaned
20839      * return {String} The cleaned HTML
20840      */
20841     cleanHtml : function(html){
20842         html = String(html);
20843         if(html.length > 5){
20844             if(Roo.isSafari){ // strip safari nonsense
20845                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20846             }
20847         }
20848         if(html == '&nbsp;'){
20849             html = '';
20850         }
20851         return html;
20852     },
20853
20854     /**
20855      * HTML Editor -> Textarea
20856      * Protected method that will not generally be called directly. Syncs the contents
20857      * of the editor iframe with the textarea.
20858      */
20859     syncValue : function(){
20860         if(this.initialized){
20861             var bd = (this.doc.body || this.doc.documentElement);
20862             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20863             var html = bd.innerHTML;
20864             if(Roo.isSafari){
20865                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20866                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20867                 if(m && m[1]){
20868                     html = '<div style="'+m[0]+'">' + html + '</div>';
20869                 }
20870             }
20871             html = this.cleanHtml(html);
20872             // fix up the special chars.. normaly like back quotes in word...
20873             // however we do not want to do this with chinese..
20874             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
20875                 
20876                 var cc = match.charCodeAt();
20877
20878                 // Get the character value, handling surrogate pairs
20879                 if (match.length == 2) {
20880                     // It's a surrogate pair, calculate the Unicode code point
20881                     var high = match.charCodeAt(0) - 0xD800;
20882                     var low  = match.charCodeAt(1) - 0xDC00;
20883                     cc = (high * 0x400) + low + 0x10000;
20884                 }  else if (
20885                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20886                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20887                     (cc >= 0xf900 && cc < 0xfb00 )
20888                 ) {
20889                         return match;
20890                 }  
20891          
20892                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
20893                 return "&#" + cc + ";";
20894                 
20895                 
20896             });
20897             
20898             
20899              
20900             if(this.owner.fireEvent('beforesync', this, html) !== false){
20901                 this.el.dom.value = html;
20902                 this.owner.fireEvent('sync', this, html);
20903             }
20904         }
20905     },
20906
20907     /**
20908      * Protected method that will not generally be called directly. Pushes the value of the textarea
20909      * into the iframe editor.
20910      */
20911     pushValue : function(){
20912         if(this.initialized){
20913             var v = this.el.dom.value.trim();
20914             
20915 //            if(v.length < 1){
20916 //                v = '&#160;';
20917 //            }
20918             
20919             if(this.owner.fireEvent('beforepush', this, v) !== false){
20920                 var d = (this.doc.body || this.doc.documentElement);
20921                 d.innerHTML = v;
20922                 this.cleanUpPaste();
20923                 this.el.dom.value = d.innerHTML;
20924                 this.owner.fireEvent('push', this, v);
20925             }
20926         }
20927     },
20928
20929     // private
20930     deferFocus : function(){
20931         this.focus.defer(10, this);
20932     },
20933
20934     // doc'ed in Field
20935     focus : function(){
20936         if(this.win && !this.sourceEditMode){
20937             this.win.focus();
20938         }else{
20939             this.el.focus();
20940         }
20941     },
20942     
20943     assignDocWin: function()
20944     {
20945         var iframe = this.iframe;
20946         
20947          if(Roo.isIE){
20948             this.doc = iframe.contentWindow.document;
20949             this.win = iframe.contentWindow;
20950         } else {
20951 //            if (!Roo.get(this.frameId)) {
20952 //                return;
20953 //            }
20954 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20955 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20956             
20957             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20958                 return;
20959             }
20960             
20961             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20962             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20963         }
20964     },
20965     
20966     // private
20967     initEditor : function(){
20968         //console.log("INIT EDITOR");
20969         this.assignDocWin();
20970         
20971         
20972         
20973         this.doc.designMode="on";
20974         this.doc.open();
20975         this.doc.write(this.getDocMarkup());
20976         this.doc.close();
20977         
20978         var dbody = (this.doc.body || this.doc.documentElement);
20979         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20980         // this copies styles from the containing element into thsi one..
20981         // not sure why we need all of this..
20982         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20983         
20984         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20985         //ss['background-attachment'] = 'fixed'; // w3c
20986         dbody.bgProperties = 'fixed'; // ie
20987         //Roo.DomHelper.applyStyles(dbody, ss);
20988         Roo.EventManager.on(this.doc, {
20989             //'mousedown': this.onEditorEvent,
20990             'mouseup': this.onEditorEvent,
20991             'dblclick': this.onEditorEvent,
20992             'click': this.onEditorEvent,
20993             'keyup': this.onEditorEvent,
20994             buffer:100,
20995             scope: this
20996         });
20997         if(Roo.isGecko){
20998             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20999         }
21000         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21001             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21002         }
21003         this.initialized = true;
21004
21005         this.owner.fireEvent('initialize', this);
21006         this.pushValue();
21007     },
21008
21009     // private
21010     onDestroy : function(){
21011         
21012         
21013         
21014         if(this.rendered){
21015             
21016             //for (var i =0; i < this.toolbars.length;i++) {
21017             //    // fixme - ask toolbars for heights?
21018             //    this.toolbars[i].onDestroy();
21019            // }
21020             
21021             //this.wrap.dom.innerHTML = '';
21022             //this.wrap.remove();
21023         }
21024     },
21025
21026     // private
21027     onFirstFocus : function(){
21028         
21029         this.assignDocWin();
21030         
21031         
21032         this.activated = true;
21033          
21034     
21035         if(Roo.isGecko){ // prevent silly gecko errors
21036             this.win.focus();
21037             var s = this.win.getSelection();
21038             if(!s.focusNode || s.focusNode.nodeType != 3){
21039                 var r = s.getRangeAt(0);
21040                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21041                 r.collapse(true);
21042                 this.deferFocus();
21043             }
21044             try{
21045                 this.execCmd('useCSS', true);
21046                 this.execCmd('styleWithCSS', false);
21047             }catch(e){}
21048         }
21049         this.owner.fireEvent('activate', this);
21050     },
21051
21052     // private
21053     adjustFont: function(btn){
21054         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21055         //if(Roo.isSafari){ // safari
21056         //    adjust *= 2;
21057        // }
21058         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21059         if(Roo.isSafari){ // safari
21060             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21061             v =  (v < 10) ? 10 : v;
21062             v =  (v > 48) ? 48 : v;
21063             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21064             
21065         }
21066         
21067         
21068         v = Math.max(1, v+adjust);
21069         
21070         this.execCmd('FontSize', v  );
21071     },
21072
21073     onEditorEvent : function(e)
21074     {
21075         this.owner.fireEvent('editorevent', this, e);
21076       //  this.updateToolbar();
21077         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21078     },
21079
21080     insertTag : function(tg)
21081     {
21082         // could be a bit smarter... -> wrap the current selected tRoo..
21083         if (tg.toLowerCase() == 'span' ||
21084             tg.toLowerCase() == 'code' ||
21085             tg.toLowerCase() == 'sup' ||
21086             tg.toLowerCase() == 'sub' 
21087             ) {
21088             
21089             range = this.createRange(this.getSelection());
21090             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21091             wrappingNode.appendChild(range.extractContents());
21092             range.insertNode(wrappingNode);
21093
21094             return;
21095             
21096             
21097             
21098         }
21099         this.execCmd("formatblock",   tg);
21100         
21101     },
21102     
21103     insertText : function(txt)
21104     {
21105         
21106         
21107         var range = this.createRange();
21108         range.deleteContents();
21109                //alert(Sender.getAttribute('label'));
21110                
21111         range.insertNode(this.doc.createTextNode(txt));
21112     } ,
21113     
21114      
21115
21116     /**
21117      * Executes a Midas editor command on the editor document and performs necessary focus and
21118      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21119      * @param {String} cmd The Midas command
21120      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21121      */
21122     relayCmd : function(cmd, value){
21123         this.win.focus();
21124         this.execCmd(cmd, value);
21125         this.owner.fireEvent('editorevent', this);
21126         //this.updateToolbar();
21127         this.owner.deferFocus();
21128     },
21129
21130     /**
21131      * Executes a Midas editor command directly on the editor document.
21132      * For visual commands, you should use {@link #relayCmd} instead.
21133      * <b>This should only be called after the editor is initialized.</b>
21134      * @param {String} cmd The Midas command
21135      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21136      */
21137     execCmd : function(cmd, value){
21138         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21139         this.syncValue();
21140     },
21141  
21142  
21143    
21144     /**
21145      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21146      * to insert tRoo.
21147      * @param {String} text | dom node.. 
21148      */
21149     insertAtCursor : function(text)
21150     {
21151         
21152         if(!this.activated){
21153             return;
21154         }
21155         /*
21156         if(Roo.isIE){
21157             this.win.focus();
21158             var r = this.doc.selection.createRange();
21159             if(r){
21160                 r.collapse(true);
21161                 r.pasteHTML(text);
21162                 this.syncValue();
21163                 this.deferFocus();
21164             
21165             }
21166             return;
21167         }
21168         */
21169         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21170             this.win.focus();
21171             
21172             
21173             // from jquery ui (MIT licenced)
21174             var range, node;
21175             var win = this.win;
21176             
21177             if (win.getSelection && win.getSelection().getRangeAt) {
21178                 range = win.getSelection().getRangeAt(0);
21179                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21180                 range.insertNode(node);
21181             } else if (win.document.selection && win.document.selection.createRange) {
21182                 // no firefox support
21183                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21184                 win.document.selection.createRange().pasteHTML(txt);
21185             } else {
21186                 // no firefox support
21187                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21188                 this.execCmd('InsertHTML', txt);
21189             } 
21190             
21191             this.syncValue();
21192             
21193             this.deferFocus();
21194         }
21195     },
21196  // private
21197     mozKeyPress : function(e){
21198         if(e.ctrlKey){
21199             var c = e.getCharCode(), cmd;
21200           
21201             if(c > 0){
21202                 c = String.fromCharCode(c).toLowerCase();
21203                 switch(c){
21204                     case 'b':
21205                         cmd = 'bold';
21206                         break;
21207                     case 'i':
21208                         cmd = 'italic';
21209                         break;
21210                     
21211                     case 'u':
21212                         cmd = 'underline';
21213                         break;
21214                     
21215                     case 'v':
21216                         this.cleanUpPaste.defer(100, this);
21217                         return;
21218                         
21219                 }
21220                 if(cmd){
21221                     this.win.focus();
21222                     this.execCmd(cmd);
21223                     this.deferFocus();
21224                     e.preventDefault();
21225                 }
21226                 
21227             }
21228         }
21229     },
21230
21231     // private
21232     fixKeys : function(){ // load time branching for fastest keydown performance
21233         if(Roo.isIE){
21234             return function(e){
21235                 var k = e.getKey(), r;
21236                 if(k == e.TAB){
21237                     e.stopEvent();
21238                     r = this.doc.selection.createRange();
21239                     if(r){
21240                         r.collapse(true);
21241                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21242                         this.deferFocus();
21243                     }
21244                     return;
21245                 }
21246                 
21247                 if(k == e.ENTER){
21248                     r = this.doc.selection.createRange();
21249                     if(r){
21250                         var target = r.parentElement();
21251                         if(!target || target.tagName.toLowerCase() != 'li'){
21252                             e.stopEvent();
21253                             r.pasteHTML('<br />');
21254                             r.collapse(false);
21255                             r.select();
21256                         }
21257                     }
21258                 }
21259                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21260                     this.cleanUpPaste.defer(100, this);
21261                     return;
21262                 }
21263                 
21264                 
21265             };
21266         }else if(Roo.isOpera){
21267             return function(e){
21268                 var k = e.getKey();
21269                 if(k == e.TAB){
21270                     e.stopEvent();
21271                     this.win.focus();
21272                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21273                     this.deferFocus();
21274                 }
21275                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21276                     this.cleanUpPaste.defer(100, this);
21277                     return;
21278                 }
21279                 
21280             };
21281         }else if(Roo.isSafari){
21282             return function(e){
21283                 var k = e.getKey();
21284                 
21285                 if(k == e.TAB){
21286                     e.stopEvent();
21287                     this.execCmd('InsertText','\t');
21288                     this.deferFocus();
21289                     return;
21290                 }
21291                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21292                     this.cleanUpPaste.defer(100, this);
21293                     return;
21294                 }
21295                 
21296              };
21297         }
21298     }(),
21299     
21300     getAllAncestors: function()
21301     {
21302         var p = this.getSelectedNode();
21303         var a = [];
21304         if (!p) {
21305             a.push(p); // push blank onto stack..
21306             p = this.getParentElement();
21307         }
21308         
21309         
21310         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21311             a.push(p);
21312             p = p.parentNode;
21313         }
21314         a.push(this.doc.body);
21315         return a;
21316     },
21317     lastSel : false,
21318     lastSelNode : false,
21319     
21320     
21321     getSelection : function() 
21322     {
21323         this.assignDocWin();
21324         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21325     },
21326     
21327     getSelectedNode: function() 
21328     {
21329         // this may only work on Gecko!!!
21330         
21331         // should we cache this!!!!
21332         
21333         
21334         
21335          
21336         var range = this.createRange(this.getSelection()).cloneRange();
21337         
21338         if (Roo.isIE) {
21339             var parent = range.parentElement();
21340             while (true) {
21341                 var testRange = range.duplicate();
21342                 testRange.moveToElementText(parent);
21343                 if (testRange.inRange(range)) {
21344                     break;
21345                 }
21346                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21347                     break;
21348                 }
21349                 parent = parent.parentElement;
21350             }
21351             return parent;
21352         }
21353         
21354         // is ancestor a text element.
21355         var ac =  range.commonAncestorContainer;
21356         if (ac.nodeType == 3) {
21357             ac = ac.parentNode;
21358         }
21359         
21360         var ar = ac.childNodes;
21361          
21362         var nodes = [];
21363         var other_nodes = [];
21364         var has_other_nodes = false;
21365         for (var i=0;i<ar.length;i++) {
21366             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21367                 continue;
21368             }
21369             // fullly contained node.
21370             
21371             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21372                 nodes.push(ar[i]);
21373                 continue;
21374             }
21375             
21376             // probably selected..
21377             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21378                 other_nodes.push(ar[i]);
21379                 continue;
21380             }
21381             // outer..
21382             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21383                 continue;
21384             }
21385             
21386             
21387             has_other_nodes = true;
21388         }
21389         if (!nodes.length && other_nodes.length) {
21390             nodes= other_nodes;
21391         }
21392         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21393             return false;
21394         }
21395         
21396         return nodes[0];
21397     },
21398     createRange: function(sel)
21399     {
21400         // this has strange effects when using with 
21401         // top toolbar - not sure if it's a great idea.
21402         //this.editor.contentWindow.focus();
21403         if (typeof sel != "undefined") {
21404             try {
21405                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21406             } catch(e) {
21407                 return this.doc.createRange();
21408             }
21409         } else {
21410             return this.doc.createRange();
21411         }
21412     },
21413     getParentElement: function()
21414     {
21415         
21416         this.assignDocWin();
21417         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21418         
21419         var range = this.createRange(sel);
21420          
21421         try {
21422             var p = range.commonAncestorContainer;
21423             while (p.nodeType == 3) { // text node
21424                 p = p.parentNode;
21425             }
21426             return p;
21427         } catch (e) {
21428             return null;
21429         }
21430     
21431     },
21432     /***
21433      *
21434      * Range intersection.. the hard stuff...
21435      *  '-1' = before
21436      *  '0' = hits..
21437      *  '1' = after.
21438      *         [ -- selected range --- ]
21439      *   [fail]                        [fail]
21440      *
21441      *    basically..
21442      *      if end is before start or  hits it. fail.
21443      *      if start is after end or hits it fail.
21444      *
21445      *   if either hits (but other is outside. - then it's not 
21446      *   
21447      *    
21448      **/
21449     
21450     
21451     // @see http://www.thismuchiknow.co.uk/?p=64.
21452     rangeIntersectsNode : function(range, node)
21453     {
21454         var nodeRange = node.ownerDocument.createRange();
21455         try {
21456             nodeRange.selectNode(node);
21457         } catch (e) {
21458             nodeRange.selectNodeContents(node);
21459         }
21460     
21461         var rangeStartRange = range.cloneRange();
21462         rangeStartRange.collapse(true);
21463     
21464         var rangeEndRange = range.cloneRange();
21465         rangeEndRange.collapse(false);
21466     
21467         var nodeStartRange = nodeRange.cloneRange();
21468         nodeStartRange.collapse(true);
21469     
21470         var nodeEndRange = nodeRange.cloneRange();
21471         nodeEndRange.collapse(false);
21472     
21473         return rangeStartRange.compareBoundaryPoints(
21474                  Range.START_TO_START, nodeEndRange) == -1 &&
21475                rangeEndRange.compareBoundaryPoints(
21476                  Range.START_TO_START, nodeStartRange) == 1;
21477         
21478          
21479     },
21480     rangeCompareNode : function(range, node)
21481     {
21482         var nodeRange = node.ownerDocument.createRange();
21483         try {
21484             nodeRange.selectNode(node);
21485         } catch (e) {
21486             nodeRange.selectNodeContents(node);
21487         }
21488         
21489         
21490         range.collapse(true);
21491     
21492         nodeRange.collapse(true);
21493      
21494         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21495         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21496          
21497         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21498         
21499         var nodeIsBefore   =  ss == 1;
21500         var nodeIsAfter    = ee == -1;
21501         
21502         if (nodeIsBefore && nodeIsAfter) {
21503             return 0; // outer
21504         }
21505         if (!nodeIsBefore && nodeIsAfter) {
21506             return 1; //right trailed.
21507         }
21508         
21509         if (nodeIsBefore && !nodeIsAfter) {
21510             return 2;  // left trailed.
21511         }
21512         // fully contined.
21513         return 3;
21514     },
21515
21516     // private? - in a new class?
21517     cleanUpPaste :  function()
21518     {
21519         // cleans up the whole document..
21520         Roo.log('cleanuppaste');
21521         
21522         this.cleanUpChildren(this.doc.body);
21523         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21524         if (clean != this.doc.body.innerHTML) {
21525             this.doc.body.innerHTML = clean;
21526         }
21527         
21528     },
21529     
21530     cleanWordChars : function(input) {// change the chars to hex code
21531         var he = Roo.HtmlEditorCore;
21532         
21533         var output = input;
21534         Roo.each(he.swapCodes, function(sw) { 
21535             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21536             
21537             output = output.replace(swapper, sw[1]);
21538         });
21539         
21540         return output;
21541     },
21542     
21543     
21544     cleanUpChildren : function (n)
21545     {
21546         if (!n.childNodes.length) {
21547             return;
21548         }
21549         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21550            this.cleanUpChild(n.childNodes[i]);
21551         }
21552     },
21553     
21554     
21555         
21556     
21557     cleanUpChild : function (node)
21558     {
21559         var ed = this;
21560         //console.log(node);
21561         if (node.nodeName == "#text") {
21562             // clean up silly Windows -- stuff?
21563             return; 
21564         }
21565         if (node.nodeName == "#comment") {
21566             if (!this.allowComments) {
21567                 node.parentNode.removeChild(node);
21568             }
21569             // clean up silly Windows -- stuff?
21570             return; 
21571         }
21572         var lcname = node.tagName.toLowerCase();
21573         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21574         // whitelist of tags..
21575         
21576         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21577             // remove node.
21578             node.parentNode.removeChild(node);
21579             return;
21580             
21581         }
21582         
21583         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21584         
21585         // spans with no attributes - just remove them..
21586         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
21587             remove_keep_children = true;
21588         }
21589         
21590         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21591         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21592         
21593         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21594         //    remove_keep_children = true;
21595         //}
21596         
21597         if (remove_keep_children) {
21598             this.cleanUpChildren(node);
21599             // inserts everything just before this node...
21600             while (node.childNodes.length) {
21601                 var cn = node.childNodes[0];
21602                 node.removeChild(cn);
21603                 node.parentNode.insertBefore(cn, node);
21604             }
21605             node.parentNode.removeChild(node);
21606             return;
21607         }
21608         
21609         if (!node.attributes || !node.attributes.length) {
21610             
21611           
21612             
21613             
21614             this.cleanUpChildren(node);
21615             return;
21616         }
21617         
21618         function cleanAttr(n,v)
21619         {
21620             
21621             if (v.match(/^\./) || v.match(/^\//)) {
21622                 return;
21623             }
21624             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21625                 return;
21626             }
21627             if (v.match(/^#/)) {
21628                 return;
21629             }
21630             if (v.match(/^\{/)) { // allow template editing.
21631                 return;
21632             }
21633 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21634             node.removeAttribute(n);
21635             
21636         }
21637         
21638         var cwhite = this.cwhite;
21639         var cblack = this.cblack;
21640             
21641         function cleanStyle(n,v)
21642         {
21643             if (v.match(/expression/)) { //XSS?? should we even bother..
21644                 node.removeAttribute(n);
21645                 return;
21646             }
21647             
21648             var parts = v.split(/;/);
21649             var clean = [];
21650             
21651             Roo.each(parts, function(p) {
21652                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21653                 if (!p.length) {
21654                     return true;
21655                 }
21656                 var l = p.split(':').shift().replace(/\s+/g,'');
21657                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21658                 
21659                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21660 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21661                     //node.removeAttribute(n);
21662                     return true;
21663                 }
21664                 //Roo.log()
21665                 // only allow 'c whitelisted system attributes'
21666                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21667 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21668                     //node.removeAttribute(n);
21669                     return true;
21670                 }
21671                 
21672                 
21673                  
21674                 
21675                 clean.push(p);
21676                 return true;
21677             });
21678             if (clean.length) { 
21679                 node.setAttribute(n, clean.join(';'));
21680             } else {
21681                 node.removeAttribute(n);
21682             }
21683             
21684         }
21685         
21686         
21687         for (var i = node.attributes.length-1; i > -1 ; i--) {
21688             var a = node.attributes[i];
21689             //console.log(a);
21690             
21691             if (a.name.toLowerCase().substr(0,2)=='on')  {
21692                 node.removeAttribute(a.name);
21693                 continue;
21694             }
21695             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21696                 node.removeAttribute(a.name);
21697                 continue;
21698             }
21699             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21700                 cleanAttr(a.name,a.value); // fixme..
21701                 continue;
21702             }
21703             if (a.name == 'style') {
21704                 cleanStyle(a.name,a.value);
21705                 continue;
21706             }
21707             /// clean up MS crap..
21708             // tecnically this should be a list of valid class'es..
21709             
21710             
21711             if (a.name == 'class') {
21712                 if (a.value.match(/^Mso/)) {
21713                     node.removeAttribute('class');
21714                 }
21715                 
21716                 if (a.value.match(/^body$/)) {
21717                     node.removeAttribute('class');
21718                 }
21719                 continue;
21720             }
21721             
21722             // style cleanup!?
21723             // class cleanup?
21724             
21725         }
21726         
21727         
21728         this.cleanUpChildren(node);
21729         
21730         
21731     },
21732     
21733     /**
21734      * Clean up MS wordisms...
21735      */
21736     cleanWord : function(node)
21737     {
21738         if (!node) {
21739             this.cleanWord(this.doc.body);
21740             return;
21741         }
21742         
21743         if(
21744                 node.nodeName == 'SPAN' &&
21745                 !node.hasAttributes() &&
21746                 node.childNodes.length == 1 &&
21747                 node.firstChild.nodeName == "#text"  
21748         ) {
21749             var textNode = node.firstChild;
21750             node.removeChild(textNode);
21751             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21752                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
21753             }
21754             node.parentNode.insertBefore(textNode, node);
21755             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21756                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
21757             }
21758             node.parentNode.removeChild(node);
21759         }
21760         
21761         if (node.nodeName == "#text") {
21762             // clean up silly Windows -- stuff?
21763             return; 
21764         }
21765         if (node.nodeName == "#comment") {
21766             node.parentNode.removeChild(node);
21767             // clean up silly Windows -- stuff?
21768             return; 
21769         }
21770         
21771         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21772             node.parentNode.removeChild(node);
21773             return;
21774         }
21775         //Roo.log(node.tagName);
21776         // remove - but keep children..
21777         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
21778             //Roo.log('-- removed');
21779             while (node.childNodes.length) {
21780                 var cn = node.childNodes[0];
21781                 node.removeChild(cn);
21782                 node.parentNode.insertBefore(cn, node);
21783                 // move node to parent - and clean it..
21784                 this.cleanWord(cn);
21785             }
21786             node.parentNode.removeChild(node);
21787             /// no need to iterate chidlren = it's got none..
21788             //this.iterateChildren(node, this.cleanWord);
21789             return;
21790         }
21791         // clean styles
21792         if (node.className.length) {
21793             
21794             var cn = node.className.split(/\W+/);
21795             var cna = [];
21796             Roo.each(cn, function(cls) {
21797                 if (cls.match(/Mso[a-zA-Z]+/)) {
21798                     return;
21799                 }
21800                 cna.push(cls);
21801             });
21802             node.className = cna.length ? cna.join(' ') : '';
21803             if (!cna.length) {
21804                 node.removeAttribute("class");
21805             }
21806         }
21807         
21808         if (node.hasAttribute("lang")) {
21809             node.removeAttribute("lang");
21810         }
21811         
21812         if (node.hasAttribute("style")) {
21813             
21814             var styles = node.getAttribute("style").split(";");
21815             var nstyle = [];
21816             Roo.each(styles, function(s) {
21817                 if (!s.match(/:/)) {
21818                     return;
21819                 }
21820                 var kv = s.split(":");
21821                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21822                     return;
21823                 }
21824                 // what ever is left... we allow.
21825                 nstyle.push(s);
21826             });
21827             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21828             if (!nstyle.length) {
21829                 node.removeAttribute('style');
21830             }
21831         }
21832         this.iterateChildren(node, this.cleanWord);
21833         
21834         
21835         
21836     },
21837     /**
21838      * iterateChildren of a Node, calling fn each time, using this as the scole..
21839      * @param {DomNode} node node to iterate children of.
21840      * @param {Function} fn method of this class to call on each item.
21841      */
21842     iterateChildren : function(node, fn)
21843     {
21844         if (!node.childNodes.length) {
21845                 return;
21846         }
21847         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21848            fn.call(this, node.childNodes[i])
21849         }
21850     },
21851     
21852     
21853     /**
21854      * cleanTableWidths.
21855      *
21856      * Quite often pasting from word etc.. results in tables with column and widths.
21857      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21858      *
21859      */
21860     cleanTableWidths : function(node)
21861     {
21862          
21863          
21864         if (!node) {
21865             this.cleanTableWidths(this.doc.body);
21866             return;
21867         }
21868         
21869         // ignore list...
21870         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21871             return; 
21872         }
21873         Roo.log(node.tagName);
21874         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21875             this.iterateChildren(node, this.cleanTableWidths);
21876             return;
21877         }
21878         if (node.hasAttribute('width')) {
21879             node.removeAttribute('width');
21880         }
21881         
21882          
21883         if (node.hasAttribute("style")) {
21884             // pretty basic...
21885             
21886             var styles = node.getAttribute("style").split(";");
21887             var nstyle = [];
21888             Roo.each(styles, function(s) {
21889                 if (!s.match(/:/)) {
21890                     return;
21891                 }
21892                 var kv = s.split(":");
21893                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21894                     return;
21895                 }
21896                 // what ever is left... we allow.
21897                 nstyle.push(s);
21898             });
21899             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21900             if (!nstyle.length) {
21901                 node.removeAttribute('style');
21902             }
21903         }
21904         
21905         this.iterateChildren(node, this.cleanTableWidths);
21906         
21907         
21908     },
21909     
21910     
21911     
21912     
21913     domToHTML : function(currentElement, depth, nopadtext) {
21914         
21915         depth = depth || 0;
21916         nopadtext = nopadtext || false;
21917     
21918         if (!currentElement) {
21919             return this.domToHTML(this.doc.body);
21920         }
21921         
21922         //Roo.log(currentElement);
21923         var j;
21924         var allText = false;
21925         var nodeName = currentElement.nodeName;
21926         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21927         
21928         if  (nodeName == '#text') {
21929             
21930             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21931         }
21932         
21933         
21934         var ret = '';
21935         if (nodeName != 'BODY') {
21936              
21937             var i = 0;
21938             // Prints the node tagName, such as <A>, <IMG>, etc
21939             if (tagName) {
21940                 var attr = [];
21941                 for(i = 0; i < currentElement.attributes.length;i++) {
21942                     // quoting?
21943                     var aname = currentElement.attributes.item(i).name;
21944                     if (!currentElement.attributes.item(i).value.length) {
21945                         continue;
21946                     }
21947                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21948                 }
21949                 
21950                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21951             } 
21952             else {
21953                 
21954                 // eack
21955             }
21956         } else {
21957             tagName = false;
21958         }
21959         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21960             return ret;
21961         }
21962         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21963             nopadtext = true;
21964         }
21965         
21966         
21967         // Traverse the tree
21968         i = 0;
21969         var currentElementChild = currentElement.childNodes.item(i);
21970         var allText = true;
21971         var innerHTML  = '';
21972         lastnode = '';
21973         while (currentElementChild) {
21974             // Formatting code (indent the tree so it looks nice on the screen)
21975             var nopad = nopadtext;
21976             if (lastnode == 'SPAN') {
21977                 nopad  = true;
21978             }
21979             // text
21980             if  (currentElementChild.nodeName == '#text') {
21981                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21982                 toadd = nopadtext ? toadd : toadd.trim();
21983                 if (!nopad && toadd.length > 80) {
21984                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21985                 }
21986                 innerHTML  += toadd;
21987                 
21988                 i++;
21989                 currentElementChild = currentElement.childNodes.item(i);
21990                 lastNode = '';
21991                 continue;
21992             }
21993             allText = false;
21994             
21995             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21996                 
21997             // Recursively traverse the tree structure of the child node
21998             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21999             lastnode = currentElementChild.nodeName;
22000             i++;
22001             currentElementChild=currentElement.childNodes.item(i);
22002         }
22003         
22004         ret += innerHTML;
22005         
22006         if (!allText) {
22007                 // The remaining code is mostly for formatting the tree
22008             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22009         }
22010         
22011         
22012         if (tagName) {
22013             ret+= "</"+tagName+">";
22014         }
22015         return ret;
22016         
22017     },
22018         
22019     applyBlacklists : function()
22020     {
22021         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22022         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22023         
22024         this.white = [];
22025         this.black = [];
22026         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22027             if (b.indexOf(tag) > -1) {
22028                 return;
22029             }
22030             this.white.push(tag);
22031             
22032         }, this);
22033         
22034         Roo.each(w, function(tag) {
22035             if (b.indexOf(tag) > -1) {
22036                 return;
22037             }
22038             if (this.white.indexOf(tag) > -1) {
22039                 return;
22040             }
22041             this.white.push(tag);
22042             
22043         }, this);
22044         
22045         
22046         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22047             if (w.indexOf(tag) > -1) {
22048                 return;
22049             }
22050             this.black.push(tag);
22051             
22052         }, this);
22053         
22054         Roo.each(b, function(tag) {
22055             if (w.indexOf(tag) > -1) {
22056                 return;
22057             }
22058             if (this.black.indexOf(tag) > -1) {
22059                 return;
22060             }
22061             this.black.push(tag);
22062             
22063         }, this);
22064         
22065         
22066         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22067         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22068         
22069         this.cwhite = [];
22070         this.cblack = [];
22071         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22072             if (b.indexOf(tag) > -1) {
22073                 return;
22074             }
22075             this.cwhite.push(tag);
22076             
22077         }, this);
22078         
22079         Roo.each(w, function(tag) {
22080             if (b.indexOf(tag) > -1) {
22081                 return;
22082             }
22083             if (this.cwhite.indexOf(tag) > -1) {
22084                 return;
22085             }
22086             this.cwhite.push(tag);
22087             
22088         }, this);
22089         
22090         
22091         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22092             if (w.indexOf(tag) > -1) {
22093                 return;
22094             }
22095             this.cblack.push(tag);
22096             
22097         }, this);
22098         
22099         Roo.each(b, function(tag) {
22100             if (w.indexOf(tag) > -1) {
22101                 return;
22102             }
22103             if (this.cblack.indexOf(tag) > -1) {
22104                 return;
22105             }
22106             this.cblack.push(tag);
22107             
22108         }, this);
22109     },
22110     
22111     setStylesheets : function(stylesheets)
22112     {
22113         if(typeof(stylesheets) == 'string'){
22114             Roo.get(this.iframe.contentDocument.head).createChild({
22115                 tag : 'link',
22116                 rel : 'stylesheet',
22117                 type : 'text/css',
22118                 href : stylesheets
22119             });
22120             
22121             return;
22122         }
22123         var _this = this;
22124      
22125         Roo.each(stylesheets, function(s) {
22126             if(!s.length){
22127                 return;
22128             }
22129             
22130             Roo.get(_this.iframe.contentDocument.head).createChild({
22131                 tag : 'link',
22132                 rel : 'stylesheet',
22133                 type : 'text/css',
22134                 href : s
22135             });
22136         });
22137
22138         
22139     },
22140     
22141     removeStylesheets : function()
22142     {
22143         var _this = this;
22144         
22145         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22146             s.remove();
22147         });
22148     },
22149     
22150     setStyle : function(style)
22151     {
22152         Roo.get(this.iframe.contentDocument.head).createChild({
22153             tag : 'style',
22154             type : 'text/css',
22155             html : style
22156         });
22157
22158         return;
22159     }
22160     
22161     // hide stuff that is not compatible
22162     /**
22163      * @event blur
22164      * @hide
22165      */
22166     /**
22167      * @event change
22168      * @hide
22169      */
22170     /**
22171      * @event focus
22172      * @hide
22173      */
22174     /**
22175      * @event specialkey
22176      * @hide
22177      */
22178     /**
22179      * @cfg {String} fieldClass @hide
22180      */
22181     /**
22182      * @cfg {String} focusClass @hide
22183      */
22184     /**
22185      * @cfg {String} autoCreate @hide
22186      */
22187     /**
22188      * @cfg {String} inputType @hide
22189      */
22190     /**
22191      * @cfg {String} invalidClass @hide
22192      */
22193     /**
22194      * @cfg {String} invalidText @hide
22195      */
22196     /**
22197      * @cfg {String} msgFx @hide
22198      */
22199     /**
22200      * @cfg {String} validateOnBlur @hide
22201      */
22202 });
22203
22204 Roo.HtmlEditorCore.white = [
22205         'area', 'br', 'img', 'input', 'hr', 'wbr',
22206         
22207        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22208        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22209        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22210        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22211        'table',   'ul',         'xmp', 
22212        
22213        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22214       'thead',   'tr', 
22215      
22216       'dir', 'menu', 'ol', 'ul', 'dl',
22217        
22218       'embed',  'object'
22219 ];
22220
22221
22222 Roo.HtmlEditorCore.black = [
22223     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22224         'applet', // 
22225         'base',   'basefont', 'bgsound', 'blink',  'body', 
22226         'frame',  'frameset', 'head',    'html',   'ilayer', 
22227         'iframe', 'layer',  'link',     'meta',    'object',   
22228         'script', 'style' ,'title',  'xml' // clean later..
22229 ];
22230 Roo.HtmlEditorCore.clean = [
22231     'script', 'style', 'title', 'xml'
22232 ];
22233 Roo.HtmlEditorCore.remove = [
22234     'font'
22235 ];
22236 // attributes..
22237
22238 Roo.HtmlEditorCore.ablack = [
22239     'on'
22240 ];
22241     
22242 Roo.HtmlEditorCore.aclean = [ 
22243     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22244 ];
22245
22246 // protocols..
22247 Roo.HtmlEditorCore.pwhite= [
22248         'http',  'https',  'mailto'
22249 ];
22250
22251 // white listed style attributes.
22252 Roo.HtmlEditorCore.cwhite= [
22253       //  'text-align', /// default is to allow most things..
22254       
22255          
22256 //        'font-size'//??
22257 ];
22258
22259 // black listed style attributes.
22260 Roo.HtmlEditorCore.cblack= [
22261       //  'font-size' -- this can be set by the project 
22262 ];
22263
22264
22265 Roo.HtmlEditorCore.swapCodes   =[ 
22266     [    8211, "&#8211;" ], 
22267     [    8212, "&#8212;" ], 
22268     [    8216,  "'" ],  
22269     [    8217, "'" ],  
22270     [    8220, '"' ],  
22271     [    8221, '"' ],  
22272     [    8226, "*" ],  
22273     [    8230, "..." ]
22274 ]; 
22275
22276     //<script type="text/javascript">
22277
22278 /*
22279  * Ext JS Library 1.1.1
22280  * Copyright(c) 2006-2007, Ext JS, LLC.
22281  * Licence LGPL
22282  * 
22283  */
22284  
22285  
22286 Roo.form.HtmlEditor = function(config){
22287     
22288     
22289     
22290     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22291     
22292     if (!this.toolbars) {
22293         this.toolbars = [];
22294     }
22295     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22296     
22297     
22298 };
22299
22300 /**
22301  * @class Roo.form.HtmlEditor
22302  * @extends Roo.form.Field
22303  * Provides a lightweight HTML Editor component.
22304  *
22305  * This has been tested on Fireforx / Chrome.. IE may not be so great..
22306  * 
22307  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22308  * supported by this editor.</b><br/><br/>
22309  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22310  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22311  */
22312 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22313     /**
22314      * @cfg {Boolean} clearUp
22315      */
22316     clearUp : true,
22317       /**
22318      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22319      */
22320     toolbars : false,
22321    
22322      /**
22323      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22324      *                        Roo.resizable.
22325      */
22326     resizable : false,
22327      /**
22328      * @cfg {Number} height (in pixels)
22329      */   
22330     height: 300,
22331    /**
22332      * @cfg {Number} width (in pixels)
22333      */   
22334     width: 500,
22335     
22336     /**
22337      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22338      * 
22339      */
22340     stylesheets: false,
22341     
22342     
22343      /**
22344      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22345      * 
22346      */
22347     cblack: false,
22348     /**
22349      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22350      * 
22351      */
22352     cwhite: false,
22353     
22354      /**
22355      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22356      * 
22357      */
22358     black: false,
22359     /**
22360      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22361      * 
22362      */
22363     white: false,
22364     /**
22365      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
22366      */
22367     allowComments: false,
22368     
22369     // id of frame..
22370     frameId: false,
22371     
22372     // private properties
22373     validationEvent : false,
22374     deferHeight: true,
22375     initialized : false,
22376     activated : false,
22377     
22378     onFocus : Roo.emptyFn,
22379     iframePad:3,
22380     hideMode:'offsets',
22381     
22382     actionMode : 'container', // defaults to hiding it...
22383     
22384     defaultAutoCreate : { // modified by initCompnoent..
22385         tag: "textarea",
22386         style:"width:500px;height:300px;",
22387         autocomplete: "new-password"
22388     },
22389
22390     // private
22391     initComponent : function(){
22392         this.addEvents({
22393             /**
22394              * @event initialize
22395              * Fires when the editor is fully initialized (including the iframe)
22396              * @param {HtmlEditor} this
22397              */
22398             initialize: true,
22399             /**
22400              * @event activate
22401              * Fires when the editor is first receives the focus. Any insertion must wait
22402              * until after this event.
22403              * @param {HtmlEditor} this
22404              */
22405             activate: true,
22406              /**
22407              * @event beforesync
22408              * Fires before the textarea is updated with content from the editor iframe. Return false
22409              * to cancel the sync.
22410              * @param {HtmlEditor} this
22411              * @param {String} html
22412              */
22413             beforesync: true,
22414              /**
22415              * @event beforepush
22416              * Fires before the iframe editor is updated with content from the textarea. Return false
22417              * to cancel the push.
22418              * @param {HtmlEditor} this
22419              * @param {String} html
22420              */
22421             beforepush: true,
22422              /**
22423              * @event sync
22424              * Fires when the textarea is updated with content from the editor iframe.
22425              * @param {HtmlEditor} this
22426              * @param {String} html
22427              */
22428             sync: true,
22429              /**
22430              * @event push
22431              * Fires when the iframe editor is updated with content from the textarea.
22432              * @param {HtmlEditor} this
22433              * @param {String} html
22434              */
22435             push: true,
22436              /**
22437              * @event editmodechange
22438              * Fires when the editor switches edit modes
22439              * @param {HtmlEditor} this
22440              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22441              */
22442             editmodechange: true,
22443             /**
22444              * @event editorevent
22445              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22446              * @param {HtmlEditor} this
22447              */
22448             editorevent: true,
22449             /**
22450              * @event firstfocus
22451              * Fires when on first focus - needed by toolbars..
22452              * @param {HtmlEditor} this
22453              */
22454             firstfocus: true,
22455             /**
22456              * @event autosave
22457              * Auto save the htmlEditor value as a file into Events
22458              * @param {HtmlEditor} this
22459              */
22460             autosave: true,
22461             /**
22462              * @event savedpreview
22463              * preview the saved version of htmlEditor
22464              * @param {HtmlEditor} this
22465              */
22466             savedpreview: true,
22467             
22468             /**
22469             * @event stylesheetsclick
22470             * Fires when press the Sytlesheets button
22471             * @param {Roo.HtmlEditorCore} this
22472             */
22473             stylesheetsclick: true
22474         });
22475         this.defaultAutoCreate =  {
22476             tag: "textarea",
22477             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22478             autocomplete: "new-password"
22479         };
22480     },
22481
22482     /**
22483      * Protected method that will not generally be called directly. It
22484      * is called when the editor creates its toolbar. Override this method if you need to
22485      * add custom toolbar buttons.
22486      * @param {HtmlEditor} editor
22487      */
22488     createToolbar : function(editor){
22489         Roo.log("create toolbars");
22490         if (!editor.toolbars || !editor.toolbars.length) {
22491             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22492         }
22493         
22494         for (var i =0 ; i < editor.toolbars.length;i++) {
22495             editor.toolbars[i] = Roo.factory(
22496                     typeof(editor.toolbars[i]) == 'string' ?
22497                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
22498                 Roo.form.HtmlEditor);
22499             editor.toolbars[i].init(editor);
22500         }
22501          
22502         
22503     },
22504
22505      
22506     // private
22507     onRender : function(ct, position)
22508     {
22509         var _t = this;
22510         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22511         
22512         this.wrap = this.el.wrap({
22513             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22514         });
22515         
22516         this.editorcore.onRender(ct, position);
22517          
22518         if (this.resizable) {
22519             this.resizeEl = new Roo.Resizable(this.wrap, {
22520                 pinned : true,
22521                 wrap: true,
22522                 dynamic : true,
22523                 minHeight : this.height,
22524                 height: this.height,
22525                 handles : this.resizable,
22526                 width: this.width,
22527                 listeners : {
22528                     resize : function(r, w, h) {
22529                         _t.onResize(w,h); // -something
22530                     }
22531                 }
22532             });
22533             
22534         }
22535         this.createToolbar(this);
22536        
22537         
22538         if(!this.width){
22539             this.setSize(this.wrap.getSize());
22540         }
22541         if (this.resizeEl) {
22542             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22543             // should trigger onReize..
22544         }
22545         
22546         this.keyNav = new Roo.KeyNav(this.el, {
22547             
22548             "tab" : function(e){
22549                 e.preventDefault();
22550                 
22551                 var value = this.getValue();
22552                 
22553                 var start = this.el.dom.selectionStart;
22554                 var end = this.el.dom.selectionEnd;
22555                 
22556                 if(!e.shiftKey){
22557                     
22558                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22559                     this.el.dom.setSelectionRange(end + 1, end + 1);
22560                     return;
22561                 }
22562                 
22563                 var f = value.substring(0, start).split("\t");
22564                 
22565                 if(f.pop().length != 0){
22566                     return;
22567                 }
22568                 
22569                 this.setValue(f.join("\t") + value.substring(end));
22570                 this.el.dom.setSelectionRange(start - 1, start - 1);
22571                 
22572             },
22573             
22574             "home" : function(e){
22575                 e.preventDefault();
22576                 
22577                 var curr = this.el.dom.selectionStart;
22578                 var lines = this.getValue().split("\n");
22579                 
22580                 if(!lines.length){
22581                     return;
22582                 }
22583                 
22584                 if(e.ctrlKey){
22585                     this.el.dom.setSelectionRange(0, 0);
22586                     return;
22587                 }
22588                 
22589                 var pos = 0;
22590                 
22591                 for (var i = 0; i < lines.length;i++) {
22592                     pos += lines[i].length;
22593                     
22594                     if(i != 0){
22595                         pos += 1;
22596                     }
22597                     
22598                     if(pos < curr){
22599                         continue;
22600                     }
22601                     
22602                     pos -= lines[i].length;
22603                     
22604                     break;
22605                 }
22606                 
22607                 if(!e.shiftKey){
22608                     this.el.dom.setSelectionRange(pos, pos);
22609                     return;
22610                 }
22611                 
22612                 this.el.dom.selectionStart = pos;
22613                 this.el.dom.selectionEnd = curr;
22614             },
22615             
22616             "end" : function(e){
22617                 e.preventDefault();
22618                 
22619                 var curr = this.el.dom.selectionStart;
22620                 var lines = this.getValue().split("\n");
22621                 
22622                 if(!lines.length){
22623                     return;
22624                 }
22625                 
22626                 if(e.ctrlKey){
22627                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22628                     return;
22629                 }
22630                 
22631                 var pos = 0;
22632                 
22633                 for (var i = 0; i < lines.length;i++) {
22634                     
22635                     pos += lines[i].length;
22636                     
22637                     if(i != 0){
22638                         pos += 1;
22639                     }
22640                     
22641                     if(pos < curr){
22642                         continue;
22643                     }
22644                     
22645                     break;
22646                 }
22647                 
22648                 if(!e.shiftKey){
22649                     this.el.dom.setSelectionRange(pos, pos);
22650                     return;
22651                 }
22652                 
22653                 this.el.dom.selectionStart = curr;
22654                 this.el.dom.selectionEnd = pos;
22655             },
22656
22657             scope : this,
22658
22659             doRelay : function(foo, bar, hname){
22660                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22661             },
22662
22663             forceKeyDown: true
22664         });
22665         
22666 //        if(this.autosave && this.w){
22667 //            this.autoSaveFn = setInterval(this.autosave, 1000);
22668 //        }
22669     },
22670
22671     // private
22672     onResize : function(w, h)
22673     {
22674         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22675         var ew = false;
22676         var eh = false;
22677         
22678         if(this.el ){
22679             if(typeof w == 'number'){
22680                 var aw = w - this.wrap.getFrameWidth('lr');
22681                 this.el.setWidth(this.adjustWidth('textarea', aw));
22682                 ew = aw;
22683             }
22684             if(typeof h == 'number'){
22685                 var tbh = 0;
22686                 for (var i =0; i < this.toolbars.length;i++) {
22687                     // fixme - ask toolbars for heights?
22688                     tbh += this.toolbars[i].tb.el.getHeight();
22689                     if (this.toolbars[i].footer) {
22690                         tbh += this.toolbars[i].footer.el.getHeight();
22691                     }
22692                 }
22693                 
22694                 
22695                 
22696                 
22697                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22698                 ah -= 5; // knock a few pixes off for look..
22699 //                Roo.log(ah);
22700                 this.el.setHeight(this.adjustWidth('textarea', ah));
22701                 var eh = ah;
22702             }
22703         }
22704         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22705         this.editorcore.onResize(ew,eh);
22706         
22707     },
22708
22709     /**
22710      * Toggles the editor between standard and source edit mode.
22711      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22712      */
22713     toggleSourceEdit : function(sourceEditMode)
22714     {
22715         this.editorcore.toggleSourceEdit(sourceEditMode);
22716         
22717         if(this.editorcore.sourceEditMode){
22718             Roo.log('editor - showing textarea');
22719             
22720 //            Roo.log('in');
22721 //            Roo.log(this.syncValue());
22722             this.editorcore.syncValue();
22723             this.el.removeClass('x-hidden');
22724             this.el.dom.removeAttribute('tabIndex');
22725             this.el.focus();
22726             
22727             for (var i = 0; i < this.toolbars.length; i++) {
22728                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22729                     this.toolbars[i].tb.hide();
22730                     this.toolbars[i].footer.hide();
22731                 }
22732             }
22733             
22734         }else{
22735             Roo.log('editor - hiding textarea');
22736 //            Roo.log('out')
22737 //            Roo.log(this.pushValue()); 
22738             this.editorcore.pushValue();
22739             
22740             this.el.addClass('x-hidden');
22741             this.el.dom.setAttribute('tabIndex', -1);
22742             
22743             for (var i = 0; i < this.toolbars.length; i++) {
22744                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22745                     this.toolbars[i].tb.show();
22746                     this.toolbars[i].footer.show();
22747                 }
22748             }
22749             
22750             //this.deferFocus();
22751         }
22752         
22753         this.setSize(this.wrap.getSize());
22754         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
22755         
22756         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22757     },
22758  
22759     // private (for BoxComponent)
22760     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22761
22762     // private (for BoxComponent)
22763     getResizeEl : function(){
22764         return this.wrap;
22765     },
22766
22767     // private (for BoxComponent)
22768     getPositionEl : function(){
22769         return this.wrap;
22770     },
22771
22772     // private
22773     initEvents : function(){
22774         this.originalValue = this.getValue();
22775     },
22776
22777     /**
22778      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22779      * @method
22780      */
22781     markInvalid : Roo.emptyFn,
22782     /**
22783      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22784      * @method
22785      */
22786     clearInvalid : Roo.emptyFn,
22787
22788     setValue : function(v){
22789         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
22790         this.editorcore.pushValue();
22791     },
22792
22793      
22794     // private
22795     deferFocus : function(){
22796         this.focus.defer(10, this);
22797     },
22798
22799     // doc'ed in Field
22800     focus : function(){
22801         this.editorcore.focus();
22802         
22803     },
22804       
22805
22806     // private
22807     onDestroy : function(){
22808         
22809         
22810         
22811         if(this.rendered){
22812             
22813             for (var i =0; i < this.toolbars.length;i++) {
22814                 // fixme - ask toolbars for heights?
22815                 this.toolbars[i].onDestroy();
22816             }
22817             
22818             this.wrap.dom.innerHTML = '';
22819             this.wrap.remove();
22820         }
22821     },
22822
22823     // private
22824     onFirstFocus : function(){
22825         //Roo.log("onFirstFocus");
22826         this.editorcore.onFirstFocus();
22827          for (var i =0; i < this.toolbars.length;i++) {
22828             this.toolbars[i].onFirstFocus();
22829         }
22830         
22831     },
22832     
22833     // private
22834     syncValue : function()
22835     {
22836         this.editorcore.syncValue();
22837     },
22838     
22839     pushValue : function()
22840     {
22841         this.editorcore.pushValue();
22842     },
22843     
22844     setStylesheets : function(stylesheets)
22845     {
22846         this.editorcore.setStylesheets(stylesheets);
22847     },
22848     
22849     removeStylesheets : function()
22850     {
22851         this.editorcore.removeStylesheets();
22852     }
22853      
22854     
22855     // hide stuff that is not compatible
22856     /**
22857      * @event blur
22858      * @hide
22859      */
22860     /**
22861      * @event change
22862      * @hide
22863      */
22864     /**
22865      * @event focus
22866      * @hide
22867      */
22868     /**
22869      * @event specialkey
22870      * @hide
22871      */
22872     /**
22873      * @cfg {String} fieldClass @hide
22874      */
22875     /**
22876      * @cfg {String} focusClass @hide
22877      */
22878     /**
22879      * @cfg {String} autoCreate @hide
22880      */
22881     /**
22882      * @cfg {String} inputType @hide
22883      */
22884     /**
22885      * @cfg {String} invalidClass @hide
22886      */
22887     /**
22888      * @cfg {String} invalidText @hide
22889      */
22890     /**
22891      * @cfg {String} msgFx @hide
22892      */
22893     /**
22894      * @cfg {String} validateOnBlur @hide
22895      */
22896 });
22897  
22898     // <script type="text/javascript">
22899 /*
22900  * Based on
22901  * Ext JS Library 1.1.1
22902  * Copyright(c) 2006-2007, Ext JS, LLC.
22903  *  
22904  
22905  */
22906
22907 /**
22908  * @class Roo.form.HtmlEditorToolbar1
22909  * Basic Toolbar
22910  * 
22911  * Usage:
22912  *
22913  new Roo.form.HtmlEditor({
22914     ....
22915     toolbars : [
22916         new Roo.form.HtmlEditorToolbar1({
22917             disable : { fonts: 1 , format: 1, ..., ... , ...],
22918             btns : [ .... ]
22919         })
22920     }
22921      
22922  * 
22923  * @cfg {Object} disable List of elements to disable..
22924  * @cfg {Array} btns List of additional buttons.
22925  * 
22926  * 
22927  * NEEDS Extra CSS? 
22928  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22929  */
22930  
22931 Roo.form.HtmlEditor.ToolbarStandard = function(config)
22932 {
22933     
22934     Roo.apply(this, config);
22935     
22936     // default disabled, based on 'good practice'..
22937     this.disable = this.disable || {};
22938     Roo.applyIf(this.disable, {
22939         fontSize : true,
22940         colors : true,
22941         specialElements : true
22942     });
22943     
22944     
22945     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22946     // dont call parent... till later.
22947 }
22948
22949 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
22950     
22951     tb: false,
22952     
22953     rendered: false,
22954     
22955     editor : false,
22956     editorcore : false,
22957     /**
22958      * @cfg {Object} disable  List of toolbar elements to disable
22959          
22960      */
22961     disable : false,
22962     
22963     
22964      /**
22965      * @cfg {String} createLinkText The default text for the create link prompt
22966      */
22967     createLinkText : 'Please enter the URL for the link:',
22968     /**
22969      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
22970      */
22971     defaultLinkValue : 'http:/'+'/',
22972    
22973     
22974       /**
22975      * @cfg {Array} fontFamilies An array of available font families
22976      */
22977     fontFamilies : [
22978         'Arial',
22979         'Courier New',
22980         'Tahoma',
22981         'Times New Roman',
22982         'Verdana'
22983     ],
22984     
22985     specialChars : [
22986            "&#169;",
22987           "&#174;",     
22988           "&#8482;",    
22989           "&#163;" ,    
22990          // "&#8212;",    
22991           "&#8230;",    
22992           "&#247;" ,    
22993         //  "&#225;" ,     ?? a acute?
22994            "&#8364;"    , //Euro
22995        //   "&#8220;"    ,
22996         //  "&#8221;"    ,
22997         //  "&#8226;"    ,
22998           "&#176;"  //   , // degrees
22999
23000          // "&#233;"     , // e ecute
23001          // "&#250;"     , // u ecute?
23002     ],
23003     
23004     specialElements : [
23005         {
23006             text: "Insert Table",
23007             xtype: 'MenuItem',
23008             xns : Roo.Menu,
23009             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
23010                 
23011         },
23012         {    
23013             text: "Insert Image",
23014             xtype: 'MenuItem',
23015             xns : Roo.Menu,
23016             ihtml : '<img src="about:blank"/>'
23017             
23018         }
23019         
23020          
23021     ],
23022     
23023     
23024     inputElements : [ 
23025             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
23026             "input:submit", "input:button", "select", "textarea", "label" ],
23027     formats : [
23028         ["p"] ,  
23029         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
23030         ["pre"],[ "code"], 
23031         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
23032         ['div'],['span'],
23033         ['sup'],['sub']
23034     ],
23035     
23036     cleanStyles : [
23037         "font-size"
23038     ],
23039      /**
23040      * @cfg {String} defaultFont default font to use.
23041      */
23042     defaultFont: 'tahoma',
23043    
23044     fontSelect : false,
23045     
23046     
23047     formatCombo : false,
23048     
23049     init : function(editor)
23050     {
23051         this.editor = editor;
23052         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23053         var editorcore = this.editorcore;
23054         
23055         var _t = this;
23056         
23057         var fid = editorcore.frameId;
23058         var etb = this;
23059         function btn(id, toggle, handler){
23060             var xid = fid + '-'+ id ;
23061             return {
23062                 id : xid,
23063                 cmd : id,
23064                 cls : 'x-btn-icon x-edit-'+id,
23065                 enableToggle:toggle !== false,
23066                 scope: _t, // was editor...
23067                 handler:handler||_t.relayBtnCmd,
23068                 clickEvent:'mousedown',
23069                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23070                 tabIndex:-1
23071             };
23072         }
23073         
23074         
23075         
23076         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
23077         this.tb = tb;
23078          // stop form submits
23079         tb.el.on('click', function(e){
23080             e.preventDefault(); // what does this do?
23081         });
23082
23083         if(!this.disable.font) { // && !Roo.isSafari){
23084             /* why no safari for fonts 
23085             editor.fontSelect = tb.el.createChild({
23086                 tag:'select',
23087                 tabIndex: -1,
23088                 cls:'x-font-select',
23089                 html: this.createFontOptions()
23090             });
23091             
23092             editor.fontSelect.on('change', function(){
23093                 var font = editor.fontSelect.dom.value;
23094                 editor.relayCmd('fontname', font);
23095                 editor.deferFocus();
23096             }, editor);
23097             
23098             tb.add(
23099                 editor.fontSelect.dom,
23100                 '-'
23101             );
23102             */
23103             
23104         };
23105         if(!this.disable.formats){
23106             this.formatCombo = new Roo.form.ComboBox({
23107                 store: new Roo.data.SimpleStore({
23108                     id : 'tag',
23109                     fields: ['tag'],
23110                     data : this.formats // from states.js
23111                 }),
23112                 blockFocus : true,
23113                 name : '',
23114                 //autoCreate : {tag: "div",  size: "20"},
23115                 displayField:'tag',
23116                 typeAhead: false,
23117                 mode: 'local',
23118                 editable : false,
23119                 triggerAction: 'all',
23120                 emptyText:'Add tag',
23121                 selectOnFocus:true,
23122                 width:135,
23123                 listeners : {
23124                     'select': function(c, r, i) {
23125                         editorcore.insertTag(r.get('tag'));
23126                         editor.focus();
23127                     }
23128                 }
23129
23130             });
23131             tb.addField(this.formatCombo);
23132             
23133         }
23134         
23135         if(!this.disable.format){
23136             tb.add(
23137                 btn('bold'),
23138                 btn('italic'),
23139                 btn('underline'),
23140                 btn('strikethrough')
23141             );
23142         };
23143         if(!this.disable.fontSize){
23144             tb.add(
23145                 '-',
23146                 
23147                 
23148                 btn('increasefontsize', false, editorcore.adjustFont),
23149                 btn('decreasefontsize', false, editorcore.adjustFont)
23150             );
23151         };
23152         
23153         
23154         if(!this.disable.colors){
23155             tb.add(
23156                 '-', {
23157                     id:editorcore.frameId +'-forecolor',
23158                     cls:'x-btn-icon x-edit-forecolor',
23159                     clickEvent:'mousedown',
23160                     tooltip: this.buttonTips['forecolor'] || undefined,
23161                     tabIndex:-1,
23162                     menu : new Roo.menu.ColorMenu({
23163                         allowReselect: true,
23164                         focus: Roo.emptyFn,
23165                         value:'000000',
23166                         plain:true,
23167                         selectHandler: function(cp, color){
23168                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
23169                             editor.deferFocus();
23170                         },
23171                         scope: editorcore,
23172                         clickEvent:'mousedown'
23173                     })
23174                 }, {
23175                     id:editorcore.frameId +'backcolor',
23176                     cls:'x-btn-icon x-edit-backcolor',
23177                     clickEvent:'mousedown',
23178                     tooltip: this.buttonTips['backcolor'] || undefined,
23179                     tabIndex:-1,
23180                     menu : new Roo.menu.ColorMenu({
23181                         focus: Roo.emptyFn,
23182                         value:'FFFFFF',
23183                         plain:true,
23184                         allowReselect: true,
23185                         selectHandler: function(cp, color){
23186                             if(Roo.isGecko){
23187                                 editorcore.execCmd('useCSS', false);
23188                                 editorcore.execCmd('hilitecolor', color);
23189                                 editorcore.execCmd('useCSS', true);
23190                                 editor.deferFocus();
23191                             }else{
23192                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
23193                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
23194                                 editor.deferFocus();
23195                             }
23196                         },
23197                         scope:editorcore,
23198                         clickEvent:'mousedown'
23199                     })
23200                 }
23201             );
23202         };
23203         // now add all the items...
23204         
23205
23206         if(!this.disable.alignments){
23207             tb.add(
23208                 '-',
23209                 btn('justifyleft'),
23210                 btn('justifycenter'),
23211                 btn('justifyright')
23212             );
23213         };
23214
23215         //if(!Roo.isSafari){
23216             if(!this.disable.links){
23217                 tb.add(
23218                     '-',
23219                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
23220                 );
23221             };
23222
23223             if(!this.disable.lists){
23224                 tb.add(
23225                     '-',
23226                     btn('insertorderedlist'),
23227                     btn('insertunorderedlist')
23228                 );
23229             }
23230             if(!this.disable.sourceEdit){
23231                 tb.add(
23232                     '-',
23233                     btn('sourceedit', true, function(btn){
23234                         this.toggleSourceEdit(btn.pressed);
23235                     })
23236                 );
23237             }
23238         //}
23239         
23240         var smenu = { };
23241         // special menu.. - needs to be tidied up..
23242         if (!this.disable.special) {
23243             smenu = {
23244                 text: "&#169;",
23245                 cls: 'x-edit-none',
23246                 
23247                 menu : {
23248                     items : []
23249                 }
23250             };
23251             for (var i =0; i < this.specialChars.length; i++) {
23252                 smenu.menu.items.push({
23253                     
23254                     html: this.specialChars[i],
23255                     handler: function(a,b) {
23256                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23257                         //editor.insertAtCursor(a.html);
23258                         
23259                     },
23260                     tabIndex:-1
23261                 });
23262             }
23263             
23264             
23265             tb.add(smenu);
23266             
23267             
23268         }
23269         
23270         var cmenu = { };
23271         if (!this.disable.cleanStyles) {
23272             cmenu = {
23273                 cls: 'x-btn-icon x-btn-clear',
23274                 
23275                 menu : {
23276                     items : []
23277                 }
23278             };
23279             for (var i =0; i < this.cleanStyles.length; i++) {
23280                 cmenu.menu.items.push({
23281                     actiontype : this.cleanStyles[i],
23282                     html: 'Remove ' + this.cleanStyles[i],
23283                     handler: function(a,b) {
23284 //                        Roo.log(a);
23285 //                        Roo.log(b);
23286                         var c = Roo.get(editorcore.doc.body);
23287                         c.select('[style]').each(function(s) {
23288                             s.dom.style.removeProperty(a.actiontype);
23289                         });
23290                         editorcore.syncValue();
23291                     },
23292                     tabIndex:-1
23293                 });
23294             }
23295              cmenu.menu.items.push({
23296                 actiontype : 'tablewidths',
23297                 html: 'Remove Table Widths',
23298                 handler: function(a,b) {
23299                     editorcore.cleanTableWidths();
23300                     editorcore.syncValue();
23301                 },
23302                 tabIndex:-1
23303             });
23304             cmenu.menu.items.push({
23305                 actiontype : 'word',
23306                 html: 'Remove MS Word Formating',
23307                 handler: function(a,b) {
23308                     editorcore.cleanWord();
23309                     editorcore.syncValue();
23310                 },
23311                 tabIndex:-1
23312             });
23313             
23314             cmenu.menu.items.push({
23315                 actiontype : 'all',
23316                 html: 'Remove All Styles',
23317                 handler: function(a,b) {
23318                     
23319                     var c = Roo.get(editorcore.doc.body);
23320                     c.select('[style]').each(function(s) {
23321                         s.dom.removeAttribute('style');
23322                     });
23323                     editorcore.syncValue();
23324                 },
23325                 tabIndex:-1
23326             });
23327             
23328             cmenu.menu.items.push({
23329                 actiontype : 'all',
23330                 html: 'Remove All CSS Classes',
23331                 handler: function(a,b) {
23332                     
23333                     var c = Roo.get(editorcore.doc.body);
23334                     c.select('[class]').each(function(s) {
23335                         s.dom.removeAttribute('class');
23336                     });
23337                     editorcore.cleanWord();
23338                     editorcore.syncValue();
23339                 },
23340                 tabIndex:-1
23341             });
23342             
23343              cmenu.menu.items.push({
23344                 actiontype : 'tidy',
23345                 html: 'Tidy HTML Source',
23346                 handler: function(a,b) {
23347                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
23348                     editorcore.syncValue();
23349                 },
23350                 tabIndex:-1
23351             });
23352             
23353             
23354             tb.add(cmenu);
23355         }
23356          
23357         if (!this.disable.specialElements) {
23358             var semenu = {
23359                 text: "Other;",
23360                 cls: 'x-edit-none',
23361                 menu : {
23362                     items : []
23363                 }
23364             };
23365             for (var i =0; i < this.specialElements.length; i++) {
23366                 semenu.menu.items.push(
23367                     Roo.apply({ 
23368                         handler: function(a,b) {
23369                             editor.insertAtCursor(this.ihtml);
23370                         }
23371                     }, this.specialElements[i])
23372                 );
23373                     
23374             }
23375             
23376             tb.add(semenu);
23377             
23378             
23379         }
23380          
23381         
23382         if (this.btns) {
23383             for(var i =0; i< this.btns.length;i++) {
23384                 var b = Roo.factory(this.btns[i],Roo.form);
23385                 b.cls =  'x-edit-none';
23386                 
23387                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23388                     b.cls += ' x-init-enable';
23389                 }
23390                 
23391                 b.scope = editorcore;
23392                 tb.add(b);
23393             }
23394         
23395         }
23396         
23397         
23398         
23399         // disable everything...
23400         
23401         this.tb.items.each(function(item){
23402             
23403            if(
23404                 item.id != editorcore.frameId+ '-sourceedit' && 
23405                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23406             ){
23407                 
23408                 item.disable();
23409             }
23410         });
23411         this.rendered = true;
23412         
23413         // the all the btns;
23414         editor.on('editorevent', this.updateToolbar, this);
23415         // other toolbars need to implement this..
23416         //editor.on('editmodechange', this.updateToolbar, this);
23417     },
23418     
23419     
23420     relayBtnCmd : function(btn) {
23421         this.editorcore.relayCmd(btn.cmd);
23422     },
23423     // private used internally
23424     createLink : function(){
23425         Roo.log("create link?");
23426         var url = prompt(this.createLinkText, this.defaultLinkValue);
23427         if(url && url != 'http:/'+'/'){
23428             this.editorcore.relayCmd('createlink', url);
23429         }
23430     },
23431
23432     
23433     /**
23434      * Protected method that will not generally be called directly. It triggers
23435      * a toolbar update by reading the markup state of the current selection in the editor.
23436      */
23437     updateToolbar: function(){
23438
23439         if(!this.editorcore.activated){
23440             this.editor.onFirstFocus();
23441             return;
23442         }
23443
23444         var btns = this.tb.items.map, 
23445             doc = this.editorcore.doc,
23446             frameId = this.editorcore.frameId;
23447
23448         if(!this.disable.font && !Roo.isSafari){
23449             /*
23450             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23451             if(name != this.fontSelect.dom.value){
23452                 this.fontSelect.dom.value = name;
23453             }
23454             */
23455         }
23456         if(!this.disable.format){
23457             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23458             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23459             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23460             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23461         }
23462         if(!this.disable.alignments){
23463             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23464             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23465             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23466         }
23467         if(!Roo.isSafari && !this.disable.lists){
23468             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23469             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23470         }
23471         
23472         var ans = this.editorcore.getAllAncestors();
23473         if (this.formatCombo) {
23474             
23475             
23476             var store = this.formatCombo.store;
23477             this.formatCombo.setValue("");
23478             for (var i =0; i < ans.length;i++) {
23479                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23480                     // select it..
23481                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23482                     break;
23483                 }
23484             }
23485         }
23486         
23487         
23488         
23489         // hides menus... - so this cant be on a menu...
23490         Roo.menu.MenuMgr.hideAll();
23491
23492         //this.editorsyncValue();
23493     },
23494    
23495     
23496     createFontOptions : function(){
23497         var buf = [], fs = this.fontFamilies, ff, lc;
23498         
23499         
23500         
23501         for(var i = 0, len = fs.length; i< len; i++){
23502             ff = fs[i];
23503             lc = ff.toLowerCase();
23504             buf.push(
23505                 '<option value="',lc,'" style="font-family:',ff,';"',
23506                     (this.defaultFont == lc ? ' selected="true">' : '>'),
23507                     ff,
23508                 '</option>'
23509             );
23510         }
23511         return buf.join('');
23512     },
23513     
23514     toggleSourceEdit : function(sourceEditMode){
23515         
23516         Roo.log("toolbar toogle");
23517         if(sourceEditMode === undefined){
23518             sourceEditMode = !this.sourceEditMode;
23519         }
23520         this.sourceEditMode = sourceEditMode === true;
23521         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23522         // just toggle the button?
23523         if(btn.pressed !== this.sourceEditMode){
23524             btn.toggle(this.sourceEditMode);
23525             return;
23526         }
23527         
23528         if(sourceEditMode){
23529             Roo.log("disabling buttons");
23530             this.tb.items.each(function(item){
23531                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23532                     item.disable();
23533                 }
23534             });
23535           
23536         }else{
23537             Roo.log("enabling buttons");
23538             if(this.editorcore.initialized){
23539                 this.tb.items.each(function(item){
23540                     item.enable();
23541                 });
23542             }
23543             
23544         }
23545         Roo.log("calling toggole on editor");
23546         // tell the editor that it's been pressed..
23547         this.editor.toggleSourceEdit(sourceEditMode);
23548        
23549     },
23550      /**
23551      * Object collection of toolbar tooltips for the buttons in the editor. The key
23552      * is the command id associated with that button and the value is a valid QuickTips object.
23553      * For example:
23554 <pre><code>
23555 {
23556     bold : {
23557         title: 'Bold (Ctrl+B)',
23558         text: 'Make the selected text bold.',
23559         cls: 'x-html-editor-tip'
23560     },
23561     italic : {
23562         title: 'Italic (Ctrl+I)',
23563         text: 'Make the selected text italic.',
23564         cls: 'x-html-editor-tip'
23565     },
23566     ...
23567 </code></pre>
23568     * @type Object
23569      */
23570     buttonTips : {
23571         bold : {
23572             title: 'Bold (Ctrl+B)',
23573             text: 'Make the selected text bold.',
23574             cls: 'x-html-editor-tip'
23575         },
23576         italic : {
23577             title: 'Italic (Ctrl+I)',
23578             text: 'Make the selected text italic.',
23579             cls: 'x-html-editor-tip'
23580         },
23581         underline : {
23582             title: 'Underline (Ctrl+U)',
23583             text: 'Underline the selected text.',
23584             cls: 'x-html-editor-tip'
23585         },
23586         strikethrough : {
23587             title: 'Strikethrough',
23588             text: 'Strikethrough the selected text.',
23589             cls: 'x-html-editor-tip'
23590         },
23591         increasefontsize : {
23592             title: 'Grow Text',
23593             text: 'Increase the font size.',
23594             cls: 'x-html-editor-tip'
23595         },
23596         decreasefontsize : {
23597             title: 'Shrink Text',
23598             text: 'Decrease the font size.',
23599             cls: 'x-html-editor-tip'
23600         },
23601         backcolor : {
23602             title: 'Text Highlight Color',
23603             text: 'Change the background color of the selected text.',
23604             cls: 'x-html-editor-tip'
23605         },
23606         forecolor : {
23607             title: 'Font Color',
23608             text: 'Change the color of the selected text.',
23609             cls: 'x-html-editor-tip'
23610         },
23611         justifyleft : {
23612             title: 'Align Text Left',
23613             text: 'Align text to the left.',
23614             cls: 'x-html-editor-tip'
23615         },
23616         justifycenter : {
23617             title: 'Center Text',
23618             text: 'Center text in the editor.',
23619             cls: 'x-html-editor-tip'
23620         },
23621         justifyright : {
23622             title: 'Align Text Right',
23623             text: 'Align text to the right.',
23624             cls: 'x-html-editor-tip'
23625         },
23626         insertunorderedlist : {
23627             title: 'Bullet List',
23628             text: 'Start a bulleted list.',
23629             cls: 'x-html-editor-tip'
23630         },
23631         insertorderedlist : {
23632             title: 'Numbered List',
23633             text: 'Start a numbered list.',
23634             cls: 'x-html-editor-tip'
23635         },
23636         createlink : {
23637             title: 'Hyperlink',
23638             text: 'Make the selected text a hyperlink.',
23639             cls: 'x-html-editor-tip'
23640         },
23641         sourceedit : {
23642             title: 'Source Edit',
23643             text: 'Switch to source editing mode.',
23644             cls: 'x-html-editor-tip'
23645         }
23646     },
23647     // private
23648     onDestroy : function(){
23649         if(this.rendered){
23650             
23651             this.tb.items.each(function(item){
23652                 if(item.menu){
23653                     item.menu.removeAll();
23654                     if(item.menu.el){
23655                         item.menu.el.destroy();
23656                     }
23657                 }
23658                 item.destroy();
23659             });
23660              
23661         }
23662     },
23663     onFirstFocus: function() {
23664         this.tb.items.each(function(item){
23665            item.enable();
23666         });
23667     }
23668 });
23669
23670
23671
23672
23673 // <script type="text/javascript">
23674 /*
23675  * Based on
23676  * Ext JS Library 1.1.1
23677  * Copyright(c) 2006-2007, Ext JS, LLC.
23678  *  
23679  
23680  */
23681
23682  
23683 /**
23684  * @class Roo.form.HtmlEditor.ToolbarContext
23685  * Context Toolbar
23686  * 
23687  * Usage:
23688  *
23689  new Roo.form.HtmlEditor({
23690     ....
23691     toolbars : [
23692         { xtype: 'ToolbarStandard', styles : {} }
23693         { xtype: 'ToolbarContext', disable : {} }
23694     ]
23695 })
23696
23697      
23698  * 
23699  * @config : {Object} disable List of elements to disable.. (not done yet.)
23700  * @config : {Object} styles  Map of styles available.
23701  * 
23702  */
23703
23704 Roo.form.HtmlEditor.ToolbarContext = function(config)
23705 {
23706     
23707     Roo.apply(this, config);
23708     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23709     // dont call parent... till later.
23710     this.styles = this.styles || {};
23711 }
23712
23713  
23714
23715 Roo.form.HtmlEditor.ToolbarContext.types = {
23716     'IMG' : {
23717         width : {
23718             title: "Width",
23719             width: 40
23720         },
23721         height:  {
23722             title: "Height",
23723             width: 40
23724         },
23725         align: {
23726             title: "Align",
23727             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
23728             width : 80
23729             
23730         },
23731         border: {
23732             title: "Border",
23733             width: 40
23734         },
23735         alt: {
23736             title: "Alt",
23737             width: 120
23738         },
23739         src : {
23740             title: "Src",
23741             width: 220
23742         }
23743         
23744     },
23745     'A' : {
23746         name : {
23747             title: "Name",
23748             width: 50
23749         },
23750         target:  {
23751             title: "Target",
23752             width: 120
23753         },
23754         href:  {
23755             title: "Href",
23756             width: 220
23757         } // border?
23758         
23759     },
23760     'TABLE' : {
23761         rows : {
23762             title: "Rows",
23763             width: 20
23764         },
23765         cols : {
23766             title: "Cols",
23767             width: 20
23768         },
23769         width : {
23770             title: "Width",
23771             width: 40
23772         },
23773         height : {
23774             title: "Height",
23775             width: 40
23776         },
23777         border : {
23778             title: "Border",
23779             width: 20
23780         }
23781     },
23782     'TD' : {
23783         width : {
23784             title: "Width",
23785             width: 40
23786         },
23787         height : {
23788             title: "Height",
23789             width: 40
23790         },   
23791         align: {
23792             title: "Align",
23793             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
23794             width: 80
23795         },
23796         valign: {
23797             title: "Valign",
23798             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
23799             width: 80
23800         },
23801         colspan: {
23802             title: "Colspan",
23803             width: 20
23804             
23805         },
23806          'font-family'  : {
23807             title : "Font",
23808             style : 'fontFamily',
23809             displayField: 'display',
23810             optname : 'font-family',
23811             width: 140
23812         }
23813     },
23814     'INPUT' : {
23815         name : {
23816             title: "name",
23817             width: 120
23818         },
23819         value : {
23820             title: "Value",
23821             width: 120
23822         },
23823         width : {
23824             title: "Width",
23825             width: 40
23826         }
23827     },
23828     'LABEL' : {
23829         'for' : {
23830             title: "For",
23831             width: 120
23832         }
23833     },
23834     'TEXTAREA' : {
23835           name : {
23836             title: "name",
23837             width: 120
23838         },
23839         rows : {
23840             title: "Rows",
23841             width: 20
23842         },
23843         cols : {
23844             title: "Cols",
23845             width: 20
23846         }
23847     },
23848     'SELECT' : {
23849         name : {
23850             title: "name",
23851             width: 120
23852         },
23853         selectoptions : {
23854             title: "Options",
23855             width: 200
23856         }
23857     },
23858     
23859     // should we really allow this??
23860     // should this just be 
23861     'BODY' : {
23862         title : {
23863             title: "Title",
23864             width: 200,
23865             disabled : true
23866         }
23867     },
23868     'SPAN' : {
23869         'font-family'  : {
23870             title : "Font",
23871             style : 'fontFamily',
23872             displayField: 'display',
23873             optname : 'font-family',
23874             width: 140
23875         }
23876     },
23877     'DIV' : {
23878         'font-family'  : {
23879             title : "Font",
23880             style : 'fontFamily',
23881             displayField: 'display',
23882             optname : 'font-family',
23883             width: 140
23884         }
23885     },
23886      'P' : {
23887         'font-family'  : {
23888             title : "Font",
23889             style : 'fontFamily',
23890             displayField: 'display',
23891             optname : 'font-family',
23892             width: 140
23893         }
23894     },
23895     
23896     '*' : {
23897         // empty..
23898     }
23899
23900 };
23901
23902 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
23903 Roo.form.HtmlEditor.ToolbarContext.stores = false;
23904
23905 Roo.form.HtmlEditor.ToolbarContext.options = {
23906         'font-family'  : [ 
23907                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
23908                 [ 'Courier New', 'Courier New'],
23909                 [ 'Tahoma', 'Tahoma'],
23910                 [ 'Times New Roman,serif', 'Times'],
23911                 [ 'Verdana','Verdana' ]
23912         ]
23913 };
23914
23915 // fixme - these need to be configurable..
23916  
23917
23918 //Roo.form.HtmlEditor.ToolbarContext.types
23919
23920
23921 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
23922     
23923     tb: false,
23924     
23925     rendered: false,
23926     
23927     editor : false,
23928     editorcore : false,
23929     /**
23930      * @cfg {Object} disable  List of toolbar elements to disable
23931          
23932      */
23933     disable : false,
23934     /**
23935      * @cfg {Object} styles List of styles 
23936      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
23937      *
23938      * These must be defined in the page, so they get rendered correctly..
23939      * .headline { }
23940      * TD.underline { }
23941      * 
23942      */
23943     styles : false,
23944     
23945     options: false,
23946     
23947     toolbars : false,
23948     
23949     init : function(editor)
23950     {
23951         this.editor = editor;
23952         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23953         var editorcore = this.editorcore;
23954         
23955         var fid = editorcore.frameId;
23956         var etb = this;
23957         function btn(id, toggle, handler){
23958             var xid = fid + '-'+ id ;
23959             return {
23960                 id : xid,
23961                 cmd : id,
23962                 cls : 'x-btn-icon x-edit-'+id,
23963                 enableToggle:toggle !== false,
23964                 scope: editorcore, // was editor...
23965                 handler:handler||editorcore.relayBtnCmd,
23966                 clickEvent:'mousedown',
23967                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23968                 tabIndex:-1
23969             };
23970         }
23971         // create a new element.
23972         var wdiv = editor.wrap.createChild({
23973                 tag: 'div'
23974             }, editor.wrap.dom.firstChild.nextSibling, true);
23975         
23976         // can we do this more than once??
23977         
23978          // stop form submits
23979       
23980  
23981         // disable everything...
23982         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23983         this.toolbars = {};
23984            
23985         for (var i in  ty) {
23986           
23987             this.toolbars[i] = this.buildToolbar(ty[i],i);
23988         }
23989         this.tb = this.toolbars.BODY;
23990         this.tb.el.show();
23991         this.buildFooter();
23992         this.footer.show();
23993         editor.on('hide', function( ) { this.footer.hide() }, this);
23994         editor.on('show', function( ) { this.footer.show() }, this);
23995         
23996          
23997         this.rendered = true;
23998         
23999         // the all the btns;
24000         editor.on('editorevent', this.updateToolbar, this);
24001         // other toolbars need to implement this..
24002         //editor.on('editmodechange', this.updateToolbar, this);
24003     },
24004     
24005     
24006     
24007     /**
24008      * Protected method that will not generally be called directly. It triggers
24009      * a toolbar update by reading the markup state of the current selection in the editor.
24010      *
24011      * Note you can force an update by calling on('editorevent', scope, false)
24012      */
24013     updateToolbar: function(editor,ev,sel){
24014
24015         //Roo.log(ev);
24016         // capture mouse up - this is handy for selecting images..
24017         // perhaps should go somewhere else...
24018         if(!this.editorcore.activated){
24019              this.editor.onFirstFocus();
24020             return;
24021         }
24022         
24023         
24024         
24025         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
24026         // selectNode - might want to handle IE?
24027         if (ev &&
24028             (ev.type == 'mouseup' || ev.type == 'click' ) &&
24029             ev.target && ev.target.tagName == 'IMG') {
24030             // they have click on an image...
24031             // let's see if we can change the selection...
24032             sel = ev.target;
24033          
24034               var nodeRange = sel.ownerDocument.createRange();
24035             try {
24036                 nodeRange.selectNode(sel);
24037             } catch (e) {
24038                 nodeRange.selectNodeContents(sel);
24039             }
24040             //nodeRange.collapse(true);
24041             var s = this.editorcore.win.getSelection();
24042             s.removeAllRanges();
24043             s.addRange(nodeRange);
24044         }  
24045         
24046       
24047         var updateFooter = sel ? false : true;
24048         
24049         
24050         var ans = this.editorcore.getAllAncestors();
24051         
24052         // pick
24053         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24054         
24055         if (!sel) { 
24056             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
24057             sel = sel ? sel : this.editorcore.doc.body;
24058             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
24059             
24060         }
24061         // pick a menu that exists..
24062         var tn = sel.tagName.toUpperCase();
24063         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
24064         
24065         tn = sel.tagName.toUpperCase();
24066         
24067         var lastSel = this.tb.selectedNode;
24068         
24069         this.tb.selectedNode = sel;
24070         
24071         // if current menu does not match..
24072         
24073         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
24074                 
24075             this.tb.el.hide();
24076             ///console.log("show: " + tn);
24077             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
24078             this.tb.el.show();
24079             // update name
24080             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
24081             
24082             
24083             // update attributes
24084             if (this.tb.fields) {
24085                 this.tb.fields.each(function(e) {
24086                     if (e.stylename) {
24087                         e.setValue(sel.style[e.stylename]);
24088                         return;
24089                     } 
24090                    e.setValue(sel.getAttribute(e.attrname));
24091                 });
24092             }
24093             
24094             var hasStyles = false;
24095             for(var i in this.styles) {
24096                 hasStyles = true;
24097                 break;
24098             }
24099             
24100             // update styles
24101             if (hasStyles) { 
24102                 var st = this.tb.fields.item(0);
24103                 
24104                 st.store.removeAll();
24105                
24106                 
24107                 var cn = sel.className.split(/\s+/);
24108                 
24109                 var avs = [];
24110                 if (this.styles['*']) {
24111                     
24112                     Roo.each(this.styles['*'], function(v) {
24113                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24114                     });
24115                 }
24116                 if (this.styles[tn]) { 
24117                     Roo.each(this.styles[tn], function(v) {
24118                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24119                     });
24120                 }
24121                 
24122                 st.store.loadData(avs);
24123                 st.collapse();
24124                 st.setValue(cn);
24125             }
24126             // flag our selected Node.
24127             this.tb.selectedNode = sel;
24128            
24129            
24130             Roo.menu.MenuMgr.hideAll();
24131
24132         }
24133         
24134         if (!updateFooter) {
24135             //this.footDisp.dom.innerHTML = ''; 
24136             return;
24137         }
24138         // update the footer
24139         //
24140         var html = '';
24141         
24142         this.footerEls = ans.reverse();
24143         Roo.each(this.footerEls, function(a,i) {
24144             if (!a) { return; }
24145             html += html.length ? ' &gt; '  :  '';
24146             
24147             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
24148             
24149         });
24150        
24151         // 
24152         var sz = this.footDisp.up('td').getSize();
24153         this.footDisp.dom.style.width = (sz.width -10) + 'px';
24154         this.footDisp.dom.style.marginLeft = '5px';
24155         
24156         this.footDisp.dom.style.overflow = 'hidden';
24157         
24158         this.footDisp.dom.innerHTML = html;
24159             
24160         //this.editorsyncValue();
24161     },
24162      
24163     
24164    
24165        
24166     // private
24167     onDestroy : function(){
24168         if(this.rendered){
24169             
24170             this.tb.items.each(function(item){
24171                 if(item.menu){
24172                     item.menu.removeAll();
24173                     if(item.menu.el){
24174                         item.menu.el.destroy();
24175                     }
24176                 }
24177                 item.destroy();
24178             });
24179              
24180         }
24181     },
24182     onFirstFocus: function() {
24183         // need to do this for all the toolbars..
24184         this.tb.items.each(function(item){
24185            item.enable();
24186         });
24187     },
24188     buildToolbar: function(tlist, nm)
24189     {
24190         var editor = this.editor;
24191         var editorcore = this.editorcore;
24192          // create a new element.
24193         var wdiv = editor.wrap.createChild({
24194                 tag: 'div'
24195             }, editor.wrap.dom.firstChild.nextSibling, true);
24196         
24197        
24198         var tb = new Roo.Toolbar(wdiv);
24199         // add the name..
24200         
24201         tb.add(nm+ ":&nbsp;");
24202         
24203         var styles = [];
24204         for(var i in this.styles) {
24205             styles.push(i);
24206         }
24207         
24208         // styles...
24209         if (styles && styles.length) {
24210             
24211             // this needs a multi-select checkbox...
24212             tb.addField( new Roo.form.ComboBox({
24213                 store: new Roo.data.SimpleStore({
24214                     id : 'val',
24215                     fields: ['val', 'selected'],
24216                     data : [] 
24217                 }),
24218                 name : '-roo-edit-className',
24219                 attrname : 'className',
24220                 displayField: 'val',
24221                 typeAhead: false,
24222                 mode: 'local',
24223                 editable : false,
24224                 triggerAction: 'all',
24225                 emptyText:'Select Style',
24226                 selectOnFocus:true,
24227                 width: 130,
24228                 listeners : {
24229                     'select': function(c, r, i) {
24230                         // initial support only for on class per el..
24231                         tb.selectedNode.className =  r ? r.get('val') : '';
24232                         editorcore.syncValue();
24233                     }
24234                 }
24235     
24236             }));
24237         }
24238         
24239         var tbc = Roo.form.HtmlEditor.ToolbarContext;
24240         var tbops = tbc.options;
24241         
24242         for (var i in tlist) {
24243             
24244             var item = tlist[i];
24245             tb.add(item.title + ":&nbsp;");
24246             
24247             
24248             //optname == used so you can configure the options available..
24249             var opts = item.opts ? item.opts : false;
24250             if (item.optname) {
24251                 opts = tbops[item.optname];
24252            
24253             }
24254             
24255             if (opts) {
24256                 // opts == pulldown..
24257                 tb.addField( new Roo.form.ComboBox({
24258                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24259                         id : 'val',
24260                         fields: ['val', 'display'],
24261                         data : opts  
24262                     }),
24263                     name : '-roo-edit-' + i,
24264                     attrname : i,
24265                     stylename : item.style ? item.style : false,
24266                     displayField: item.displayField ? item.displayField : 'val',
24267                     valueField :  'val',
24268                     typeAhead: false,
24269                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
24270                     editable : false,
24271                     triggerAction: 'all',
24272                     emptyText:'Select',
24273                     selectOnFocus:true,
24274                     width: item.width ? item.width  : 130,
24275                     listeners : {
24276                         'select': function(c, r, i) {
24277                             if (c.stylename) {
24278                                 tb.selectedNode.style[c.stylename] =  r.get('val');
24279                                 return;
24280                             }
24281                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24282                         }
24283                     }
24284
24285                 }));
24286                 continue;
24287                     
24288                  
24289                 
24290                 tb.addField( new Roo.form.TextField({
24291                     name: i,
24292                     width: 100,
24293                     //allowBlank:false,
24294                     value: ''
24295                 }));
24296                 continue;
24297             }
24298             tb.addField( new Roo.form.TextField({
24299                 name: '-roo-edit-' + i,
24300                 attrname : i,
24301                 
24302                 width: item.width,
24303                 //allowBlank:true,
24304                 value: '',
24305                 listeners: {
24306                     'change' : function(f, nv, ov) {
24307                         tb.selectedNode.setAttribute(f.attrname, nv);
24308                         editorcore.syncValue();
24309                     }
24310                 }
24311             }));
24312              
24313         }
24314         
24315         var _this = this;
24316         
24317         if(nm == 'BODY'){
24318             tb.addSeparator();
24319         
24320             tb.addButton( {
24321                 text: 'Stylesheets',
24322
24323                 listeners : {
24324                     click : function ()
24325                     {
24326                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
24327                     }
24328                 }
24329             });
24330         }
24331         
24332         tb.addFill();
24333         tb.addButton( {
24334             text: 'Remove Tag',
24335     
24336             listeners : {
24337                 click : function ()
24338                 {
24339                     // remove
24340                     // undo does not work.
24341                      
24342                     var sn = tb.selectedNode;
24343                     
24344                     var pn = sn.parentNode;
24345                     
24346                     var stn =  sn.childNodes[0];
24347                     var en = sn.childNodes[sn.childNodes.length - 1 ];
24348                     while (sn.childNodes.length) {
24349                         var node = sn.childNodes[0];
24350                         sn.removeChild(node);
24351                         //Roo.log(node);
24352                         pn.insertBefore(node, sn);
24353                         
24354                     }
24355                     pn.removeChild(sn);
24356                     var range = editorcore.createRange();
24357         
24358                     range.setStart(stn,0);
24359                     range.setEnd(en,0); //????
24360                     //range.selectNode(sel);
24361                     
24362                     
24363                     var selection = editorcore.getSelection();
24364                     selection.removeAllRanges();
24365                     selection.addRange(range);
24366                     
24367                     
24368                     
24369                     //_this.updateToolbar(null, null, pn);
24370                     _this.updateToolbar(null, null, null);
24371                     _this.footDisp.dom.innerHTML = ''; 
24372                 }
24373             }
24374             
24375                     
24376                 
24377             
24378         });
24379         
24380         
24381         tb.el.on('click', function(e){
24382             e.preventDefault(); // what does this do?
24383         });
24384         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24385         tb.el.hide();
24386         tb.name = nm;
24387         // dont need to disable them... as they will get hidden
24388         return tb;
24389          
24390         
24391     },
24392     buildFooter : function()
24393     {
24394         
24395         var fel = this.editor.wrap.createChild();
24396         this.footer = new Roo.Toolbar(fel);
24397         // toolbar has scrolly on left / right?
24398         var footDisp= new Roo.Toolbar.Fill();
24399         var _t = this;
24400         this.footer.add(
24401             {
24402                 text : '&lt;',
24403                 xtype: 'Button',
24404                 handler : function() {
24405                     _t.footDisp.scrollTo('left',0,true)
24406                 }
24407             }
24408         );
24409         this.footer.add( footDisp );
24410         this.footer.add( 
24411             {
24412                 text : '&gt;',
24413                 xtype: 'Button',
24414                 handler : function() {
24415                     // no animation..
24416                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24417                 }
24418             }
24419         );
24420         var fel = Roo.get(footDisp.el);
24421         fel.addClass('x-editor-context');
24422         this.footDispWrap = fel; 
24423         this.footDispWrap.overflow  = 'hidden';
24424         
24425         this.footDisp = fel.createChild();
24426         this.footDispWrap.on('click', this.onContextClick, this)
24427         
24428         
24429     },
24430     onContextClick : function (ev,dom)
24431     {
24432         ev.preventDefault();
24433         var  cn = dom.className;
24434         //Roo.log(cn);
24435         if (!cn.match(/x-ed-loc-/)) {
24436             return;
24437         }
24438         var n = cn.split('-').pop();
24439         var ans = this.footerEls;
24440         var sel = ans[n];
24441         
24442          // pick
24443         var range = this.editorcore.createRange();
24444         
24445         range.selectNodeContents(sel);
24446         //range.selectNode(sel);
24447         
24448         
24449         var selection = this.editorcore.getSelection();
24450         selection.removeAllRanges();
24451         selection.addRange(range);
24452         
24453         
24454         
24455         this.updateToolbar(null, null, sel);
24456         
24457         
24458     }
24459     
24460     
24461     
24462     
24463     
24464 });
24465
24466
24467
24468
24469
24470 /*
24471  * Based on:
24472  * Ext JS Library 1.1.1
24473  * Copyright(c) 2006-2007, Ext JS, LLC.
24474  *
24475  * Originally Released Under LGPL - original licence link has changed is not relivant.
24476  *
24477  * Fork - LGPL
24478  * <script type="text/javascript">
24479  */
24480  
24481 /**
24482  * @class Roo.form.BasicForm
24483  * @extends Roo.util.Observable
24484  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24485  * @constructor
24486  * @param {String/HTMLElement/Roo.Element} el The form element or its id
24487  * @param {Object} config Configuration options
24488  */
24489 Roo.form.BasicForm = function(el, config){
24490     this.allItems = [];
24491     this.childForms = [];
24492     Roo.apply(this, config);
24493     /*
24494      * The Roo.form.Field items in this form.
24495      * @type MixedCollection
24496      */
24497      
24498      
24499     this.items = new Roo.util.MixedCollection(false, function(o){
24500         return o.id || (o.id = Roo.id());
24501     });
24502     this.addEvents({
24503         /**
24504          * @event beforeaction
24505          * Fires before any action is performed. Return false to cancel the action.
24506          * @param {Form} this
24507          * @param {Action} action The action to be performed
24508          */
24509         beforeaction: true,
24510         /**
24511          * @event actionfailed
24512          * Fires when an action fails.
24513          * @param {Form} this
24514          * @param {Action} action The action that failed
24515          */
24516         actionfailed : true,
24517         /**
24518          * @event actioncomplete
24519          * Fires when an action is completed.
24520          * @param {Form} this
24521          * @param {Action} action The action that completed
24522          */
24523         actioncomplete : true
24524     });
24525     if(el){
24526         this.initEl(el);
24527     }
24528     Roo.form.BasicForm.superclass.constructor.call(this);
24529     
24530     Roo.form.BasicForm.popover.apply();
24531 };
24532
24533 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24534     /**
24535      * @cfg {String} method
24536      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24537      */
24538     /**
24539      * @cfg {DataReader} reader
24540      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24541      * This is optional as there is built-in support for processing JSON.
24542      */
24543     /**
24544      * @cfg {DataReader} errorReader
24545      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24546      * This is completely optional as there is built-in support for processing JSON.
24547      */
24548     /**
24549      * @cfg {String} url
24550      * The URL to use for form actions if one isn't supplied in the action options.
24551      */
24552     /**
24553      * @cfg {Boolean} fileUpload
24554      * Set to true if this form is a file upload.
24555      */
24556      
24557     /**
24558      * @cfg {Object} baseParams
24559      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24560      */
24561      /**
24562      
24563     /**
24564      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24565      */
24566     timeout: 30,
24567
24568     // private
24569     activeAction : null,
24570
24571     /**
24572      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24573      * or setValues() data instead of when the form was first created.
24574      */
24575     trackResetOnLoad : false,
24576     
24577     
24578     /**
24579      * childForms - used for multi-tab forms
24580      * @type {Array}
24581      */
24582     childForms : false,
24583     
24584     /**
24585      * allItems - full list of fields.
24586      * @type {Array}
24587      */
24588     allItems : false,
24589     
24590     /**
24591      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24592      * element by passing it or its id or mask the form itself by passing in true.
24593      * @type Mixed
24594      */
24595     waitMsgTarget : false,
24596     
24597     /**
24598      * @type Boolean
24599      */
24600     disableMask : false,
24601     
24602     /**
24603      * @cfg {Boolean} errorMask (true|false) default false
24604      */
24605     errorMask : false,
24606     
24607     /**
24608      * @cfg {Number} maskOffset Default 100
24609      */
24610     maskOffset : 100,
24611
24612     // private
24613     initEl : function(el){
24614         this.el = Roo.get(el);
24615         this.id = this.el.id || Roo.id();
24616         this.el.on('submit', this.onSubmit, this);
24617         this.el.addClass('x-form');
24618     },
24619
24620     // private
24621     onSubmit : function(e){
24622         e.stopEvent();
24623     },
24624
24625     /**
24626      * Returns true if client-side validation on the form is successful.
24627      * @return Boolean
24628      */
24629     isValid : function(){
24630         var valid = true;
24631         var target = false;
24632         this.items.each(function(f){
24633             if(f.validate()){
24634                 return;
24635             }
24636             
24637             valid = false;
24638                 
24639             if(!target && f.el.isVisible(true)){
24640                 target = f;
24641             }
24642         });
24643         
24644         if(this.errorMask && !valid){
24645             Roo.form.BasicForm.popover.mask(this, target);
24646         }
24647         
24648         return valid;
24649     },
24650     /**
24651      * Returns array of invalid form fields.
24652      * @return Array
24653      */
24654     
24655     invalidFields : function()
24656     {
24657         var ret = [];
24658         this.items.each(function(f){
24659             if(f.validate()){
24660                 return;
24661             }
24662             ret.push(f);
24663             
24664         });
24665         
24666         return ret;
24667     },
24668     
24669     
24670     /**
24671      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
24672      * @return Boolean
24673      */
24674     isDirty : function(){
24675         var dirty = false;
24676         this.items.each(function(f){
24677            if(f.isDirty()){
24678                dirty = true;
24679                return false;
24680            }
24681         });
24682         return dirty;
24683     },
24684     
24685     /**
24686      * Returns true if any fields in this form have changed since their original load. (New version)
24687      * @return Boolean
24688      */
24689     
24690     hasChanged : function()
24691     {
24692         var dirty = false;
24693         this.items.each(function(f){
24694            if(f.hasChanged()){
24695                dirty = true;
24696                return false;
24697            }
24698         });
24699         return dirty;
24700         
24701     },
24702     /**
24703      * Resets all hasChanged to 'false' -
24704      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24705      * So hasChanged storage is only to be used for this purpose
24706      * @return Boolean
24707      */
24708     resetHasChanged : function()
24709     {
24710         this.items.each(function(f){
24711            f.resetHasChanged();
24712         });
24713         
24714     },
24715     
24716     
24717     /**
24718      * Performs a predefined action (submit or load) or custom actions you define on this form.
24719      * @param {String} actionName The name of the action type
24720      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
24721      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24722      * accept other config options):
24723      * <pre>
24724 Property          Type             Description
24725 ----------------  ---------------  ----------------------------------------------------------------------------------
24726 url               String           The url for the action (defaults to the form's url)
24727 method            String           The form method to use (defaults to the form's method, or POST if not defined)
24728 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
24729 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
24730                                    validate the form on the client (defaults to false)
24731      * </pre>
24732      * @return {BasicForm} this
24733      */
24734     doAction : function(action, options){
24735         if(typeof action == 'string'){
24736             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
24737         }
24738         if(this.fireEvent('beforeaction', this, action) !== false){
24739             this.beforeAction(action);
24740             action.run.defer(100, action);
24741         }
24742         return this;
24743     },
24744
24745     /**
24746      * Shortcut to do a submit action.
24747      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24748      * @return {BasicForm} this
24749      */
24750     submit : function(options){
24751         this.doAction('submit', options);
24752         return this;
24753     },
24754
24755     /**
24756      * Shortcut to do a load action.
24757      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24758      * @return {BasicForm} this
24759      */
24760     load : function(options){
24761         this.doAction('load', options);
24762         return this;
24763     },
24764
24765     /**
24766      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
24767      * @param {Record} record The record to edit
24768      * @return {BasicForm} this
24769      */
24770     updateRecord : function(record){
24771         record.beginEdit();
24772         var fs = record.fields;
24773         fs.each(function(f){
24774             var field = this.findField(f.name);
24775             if(field){
24776                 record.set(f.name, field.getValue());
24777             }
24778         }, this);
24779         record.endEdit();
24780         return this;
24781     },
24782
24783     /**
24784      * Loads an Roo.data.Record into this form.
24785      * @param {Record} record The record to load
24786      * @return {BasicForm} this
24787      */
24788     loadRecord : function(record){
24789         this.setValues(record.data);
24790         return this;
24791     },
24792
24793     // private
24794     beforeAction : function(action){
24795         var o = action.options;
24796         
24797         if(!this.disableMask) {
24798             if(this.waitMsgTarget === true){
24799                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
24800             }else if(this.waitMsgTarget){
24801                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
24802                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
24803             }else {
24804                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
24805             }
24806         }
24807         
24808          
24809     },
24810
24811     // private
24812     afterAction : function(action, success){
24813         this.activeAction = null;
24814         var o = action.options;
24815         
24816         if(!this.disableMask) {
24817             if(this.waitMsgTarget === true){
24818                 this.el.unmask();
24819             }else if(this.waitMsgTarget){
24820                 this.waitMsgTarget.unmask();
24821             }else{
24822                 Roo.MessageBox.updateProgress(1);
24823                 Roo.MessageBox.hide();
24824             }
24825         }
24826         
24827         if(success){
24828             if(o.reset){
24829                 this.reset();
24830             }
24831             Roo.callback(o.success, o.scope, [this, action]);
24832             this.fireEvent('actioncomplete', this, action);
24833             
24834         }else{
24835             
24836             // failure condition..
24837             // we have a scenario where updates need confirming.
24838             // eg. if a locking scenario exists..
24839             // we look for { errors : { needs_confirm : true }} in the response.
24840             if (
24841                 (typeof(action.result) != 'undefined')  &&
24842                 (typeof(action.result.errors) != 'undefined')  &&
24843                 (typeof(action.result.errors.needs_confirm) != 'undefined')
24844            ){
24845                 var _t = this;
24846                 Roo.MessageBox.confirm(
24847                     "Change requires confirmation",
24848                     action.result.errorMsg,
24849                     function(r) {
24850                         if (r != 'yes') {
24851                             return;
24852                         }
24853                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
24854                     }
24855                     
24856                 );
24857                 
24858                 
24859                 
24860                 return;
24861             }
24862             
24863             Roo.callback(o.failure, o.scope, [this, action]);
24864             // show an error message if no failed handler is set..
24865             if (!this.hasListener('actionfailed')) {
24866                 Roo.MessageBox.alert("Error",
24867                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
24868                         action.result.errorMsg :
24869                         "Saving Failed, please check your entries or try again"
24870                 );
24871             }
24872             
24873             this.fireEvent('actionfailed', this, action);
24874         }
24875         
24876     },
24877
24878     /**
24879      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
24880      * @param {String} id The value to search for
24881      * @return Field
24882      */
24883     findField : function(id){
24884         var field = this.items.get(id);
24885         if(!field){
24886             this.items.each(function(f){
24887                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
24888                     field = f;
24889                     return false;
24890                 }
24891             });
24892         }
24893         return field || null;
24894     },
24895
24896     /**
24897      * Add a secondary form to this one, 
24898      * Used to provide tabbed forms. One form is primary, with hidden values 
24899      * which mirror the elements from the other forms.
24900      * 
24901      * @param {Roo.form.Form} form to add.
24902      * 
24903      */
24904     addForm : function(form)
24905     {
24906        
24907         if (this.childForms.indexOf(form) > -1) {
24908             // already added..
24909             return;
24910         }
24911         this.childForms.push(form);
24912         var n = '';
24913         Roo.each(form.allItems, function (fe) {
24914             
24915             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
24916             if (this.findField(n)) { // already added..
24917                 return;
24918             }
24919             var add = new Roo.form.Hidden({
24920                 name : n
24921             });
24922             add.render(this.el);
24923             
24924             this.add( add );
24925         }, this);
24926         
24927     },
24928     /**
24929      * Mark fields in this form invalid in bulk.
24930      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
24931      * @return {BasicForm} this
24932      */
24933     markInvalid : function(errors){
24934         if(errors instanceof Array){
24935             for(var i = 0, len = errors.length; i < len; i++){
24936                 var fieldError = errors[i];
24937                 var f = this.findField(fieldError.id);
24938                 if(f){
24939                     f.markInvalid(fieldError.msg);
24940                 }
24941             }
24942         }else{
24943             var field, id;
24944             for(id in errors){
24945                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
24946                     field.markInvalid(errors[id]);
24947                 }
24948             }
24949         }
24950         Roo.each(this.childForms || [], function (f) {
24951             f.markInvalid(errors);
24952         });
24953         
24954         return this;
24955     },
24956
24957     /**
24958      * Set values for fields in this form in bulk.
24959      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
24960      * @return {BasicForm} this
24961      */
24962     setValues : function(values){
24963         if(values instanceof Array){ // array of objects
24964             for(var i = 0, len = values.length; i < len; i++){
24965                 var v = values[i];
24966                 var f = this.findField(v.id);
24967                 if(f){
24968                     f.setValue(v.value);
24969                     if(this.trackResetOnLoad){
24970                         f.originalValue = f.getValue();
24971                     }
24972                 }
24973             }
24974         }else{ // object hash
24975             var field, id;
24976             for(id in values){
24977                 if(typeof values[id] != 'function' && (field = this.findField(id))){
24978                     
24979                     if (field.setFromData && 
24980                         field.valueField && 
24981                         field.displayField &&
24982                         // combos' with local stores can 
24983                         // be queried via setValue()
24984                         // to set their value..
24985                         (field.store && !field.store.isLocal)
24986                         ) {
24987                         // it's a combo
24988                         var sd = { };
24989                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
24990                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
24991                         field.setFromData(sd);
24992                         
24993                     } else {
24994                         field.setValue(values[id]);
24995                     }
24996                     
24997                     
24998                     if(this.trackResetOnLoad){
24999                         field.originalValue = field.getValue();
25000                     }
25001                 }
25002             }
25003         }
25004         this.resetHasChanged();
25005         
25006         
25007         Roo.each(this.childForms || [], function (f) {
25008             f.setValues(values);
25009             f.resetHasChanged();
25010         });
25011                 
25012         return this;
25013     },
25014  
25015     /**
25016      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
25017      * they are returned as an array.
25018      * @param {Boolean} asString
25019      * @return {Object}
25020      */
25021     getValues : function(asString){
25022         if (this.childForms) {
25023             // copy values from the child forms
25024             Roo.each(this.childForms, function (f) {
25025                 this.setValues(f.getValues());
25026             }, this);
25027         }
25028         
25029         // use formdata
25030         if (typeof(FormData) != 'undefined' && asString !== true) {
25031             // this relies on a 'recent' version of chrome apparently...
25032             try {
25033                 var fd = (new FormData(this.el.dom)).entries();
25034                 var ret = {};
25035                 var ent = fd.next();
25036                 while (!ent.done) {
25037                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
25038                     ent = fd.next();
25039                 };
25040                 return ret;
25041             } catch(e) {
25042                 
25043             }
25044             
25045         }
25046         
25047         
25048         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
25049         if(asString === true){
25050             return fs;
25051         }
25052         return Roo.urlDecode(fs);
25053     },
25054     
25055     /**
25056      * Returns the fields in this form as an object with key/value pairs. 
25057      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
25058      * @return {Object}
25059      */
25060     getFieldValues : function(with_hidden)
25061     {
25062         if (this.childForms) {
25063             // copy values from the child forms
25064             // should this call getFieldValues - probably not as we do not currently copy
25065             // hidden fields when we generate..
25066             Roo.each(this.childForms, function (f) {
25067                 this.setValues(f.getValues());
25068             }, this);
25069         }
25070         
25071         var ret = {};
25072         this.items.each(function(f){
25073             if (!f.getName()) {
25074                 return;
25075             }
25076             var v = f.getValue();
25077             if (f.inputType =='radio') {
25078                 if (typeof(ret[f.getName()]) == 'undefined') {
25079                     ret[f.getName()] = ''; // empty..
25080                 }
25081                 
25082                 if (!f.el.dom.checked) {
25083                     return;
25084                     
25085                 }
25086                 v = f.el.dom.value;
25087                 
25088             }
25089             
25090             // not sure if this supported any more..
25091             if ((typeof(v) == 'object') && f.getRawValue) {
25092                 v = f.getRawValue() ; // dates..
25093             }
25094             // combo boxes where name != hiddenName...
25095             if (f.name != f.getName()) {
25096                 ret[f.name] = f.getRawValue();
25097             }
25098             ret[f.getName()] = v;
25099         });
25100         
25101         return ret;
25102     },
25103
25104     /**
25105      * Clears all invalid messages in this form.
25106      * @return {BasicForm} this
25107      */
25108     clearInvalid : function(){
25109         this.items.each(function(f){
25110            f.clearInvalid();
25111         });
25112         
25113         Roo.each(this.childForms || [], function (f) {
25114             f.clearInvalid();
25115         });
25116         
25117         
25118         return this;
25119     },
25120
25121     /**
25122      * Resets this form.
25123      * @return {BasicForm} this
25124      */
25125     reset : function(){
25126         this.items.each(function(f){
25127             f.reset();
25128         });
25129         
25130         Roo.each(this.childForms || [], function (f) {
25131             f.reset();
25132         });
25133         this.resetHasChanged();
25134         
25135         return this;
25136     },
25137
25138     /**
25139      * Add Roo.form components to this form.
25140      * @param {Field} field1
25141      * @param {Field} field2 (optional)
25142      * @param {Field} etc (optional)
25143      * @return {BasicForm} this
25144      */
25145     add : function(){
25146         this.items.addAll(Array.prototype.slice.call(arguments, 0));
25147         return this;
25148     },
25149
25150
25151     /**
25152      * Removes a field from the items collection (does NOT remove its markup).
25153      * @param {Field} field
25154      * @return {BasicForm} this
25155      */
25156     remove : function(field){
25157         this.items.remove(field);
25158         return this;
25159     },
25160
25161     /**
25162      * Looks at the fields in this form, checks them for an id attribute,
25163      * and calls applyTo on the existing dom element with that id.
25164      * @return {BasicForm} this
25165      */
25166     render : function(){
25167         this.items.each(function(f){
25168             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
25169                 f.applyTo(f.id);
25170             }
25171         });
25172         return this;
25173     },
25174
25175     /**
25176      * Calls {@link Ext#apply} for all fields in this form with the passed object.
25177      * @param {Object} values
25178      * @return {BasicForm} this
25179      */
25180     applyToFields : function(o){
25181         this.items.each(function(f){
25182            Roo.apply(f, o);
25183         });
25184         return this;
25185     },
25186
25187     /**
25188      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
25189      * @param {Object} values
25190      * @return {BasicForm} this
25191      */
25192     applyIfToFields : function(o){
25193         this.items.each(function(f){
25194            Roo.applyIf(f, o);
25195         });
25196         return this;
25197     }
25198 });
25199
25200 // back compat
25201 Roo.BasicForm = Roo.form.BasicForm;
25202
25203 Roo.apply(Roo.form.BasicForm, {
25204     
25205     popover : {
25206         
25207         padding : 5,
25208         
25209         isApplied : false,
25210         
25211         isMasked : false,
25212         
25213         form : false,
25214         
25215         target : false,
25216         
25217         intervalID : false,
25218         
25219         maskEl : false,
25220         
25221         apply : function()
25222         {
25223             if(this.isApplied){
25224                 return;
25225             }
25226             
25227             this.maskEl = {
25228                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
25229                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
25230                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
25231                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
25232             };
25233             
25234             this.maskEl.top.enableDisplayMode("block");
25235             this.maskEl.left.enableDisplayMode("block");
25236             this.maskEl.bottom.enableDisplayMode("block");
25237             this.maskEl.right.enableDisplayMode("block");
25238             
25239             Roo.get(document.body).on('click', function(){
25240                 this.unmask();
25241             }, this);
25242             
25243             Roo.get(document.body).on('touchstart', function(){
25244                 this.unmask();
25245             }, this);
25246             
25247             this.isApplied = true
25248         },
25249         
25250         mask : function(form, target)
25251         {
25252             this.form = form;
25253             
25254             this.target = target;
25255             
25256             if(!this.form.errorMask || !target.el){
25257                 return;
25258             }
25259             
25260             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
25261             
25262             var ot = this.target.el.calcOffsetsTo(scrollable);
25263             
25264             var scrollTo = ot[1] - this.form.maskOffset;
25265             
25266             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
25267             
25268             scrollable.scrollTo('top', scrollTo);
25269             
25270             var el = this.target.wrap || this.target.el;
25271             
25272             var box = el.getBox();
25273             
25274             this.maskEl.top.setStyle('position', 'absolute');
25275             this.maskEl.top.setStyle('z-index', 10000);
25276             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
25277             this.maskEl.top.setLeft(0);
25278             this.maskEl.top.setTop(0);
25279             this.maskEl.top.show();
25280             
25281             this.maskEl.left.setStyle('position', 'absolute');
25282             this.maskEl.left.setStyle('z-index', 10000);
25283             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
25284             this.maskEl.left.setLeft(0);
25285             this.maskEl.left.setTop(box.y - this.padding);
25286             this.maskEl.left.show();
25287
25288             this.maskEl.bottom.setStyle('position', 'absolute');
25289             this.maskEl.bottom.setStyle('z-index', 10000);
25290             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
25291             this.maskEl.bottom.setLeft(0);
25292             this.maskEl.bottom.setTop(box.bottom + this.padding);
25293             this.maskEl.bottom.show();
25294
25295             this.maskEl.right.setStyle('position', 'absolute');
25296             this.maskEl.right.setStyle('z-index', 10000);
25297             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
25298             this.maskEl.right.setLeft(box.right + this.padding);
25299             this.maskEl.right.setTop(box.y - this.padding);
25300             this.maskEl.right.show();
25301
25302             this.intervalID = window.setInterval(function() {
25303                 Roo.form.BasicForm.popover.unmask();
25304             }, 10000);
25305
25306             window.onwheel = function(){ return false;};
25307             
25308             (function(){ this.isMasked = true; }).defer(500, this);
25309             
25310         },
25311         
25312         unmask : function()
25313         {
25314             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
25315                 return;
25316             }
25317             
25318             this.maskEl.top.setStyle('position', 'absolute');
25319             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
25320             this.maskEl.top.hide();
25321
25322             this.maskEl.left.setStyle('position', 'absolute');
25323             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
25324             this.maskEl.left.hide();
25325
25326             this.maskEl.bottom.setStyle('position', 'absolute');
25327             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
25328             this.maskEl.bottom.hide();
25329
25330             this.maskEl.right.setStyle('position', 'absolute');
25331             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
25332             this.maskEl.right.hide();
25333             
25334             window.onwheel = function(){ return true;};
25335             
25336             if(this.intervalID){
25337                 window.clearInterval(this.intervalID);
25338                 this.intervalID = false;
25339             }
25340             
25341             this.isMasked = false;
25342             
25343         }
25344         
25345     }
25346     
25347 });/*
25348  * Based on:
25349  * Ext JS Library 1.1.1
25350  * Copyright(c) 2006-2007, Ext JS, LLC.
25351  *
25352  * Originally Released Under LGPL - original licence link has changed is not relivant.
25353  *
25354  * Fork - LGPL
25355  * <script type="text/javascript">
25356  */
25357
25358 /**
25359  * @class Roo.form.Form
25360  * @extends Roo.form.BasicForm
25361  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
25362  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
25363  * @constructor
25364  * @param {Object} config Configuration options
25365  */
25366 Roo.form.Form = function(config){
25367     var xitems =  [];
25368     if (config.items) {
25369         xitems = config.items;
25370         delete config.items;
25371     }
25372    
25373     
25374     Roo.form.Form.superclass.constructor.call(this, null, config);
25375     this.url = this.url || this.action;
25376     if(!this.root){
25377         this.root = new Roo.form.Layout(Roo.applyIf({
25378             id: Roo.id()
25379         }, config));
25380     }
25381     this.active = this.root;
25382     /**
25383      * Array of all the buttons that have been added to this form via {@link addButton}
25384      * @type Array
25385      */
25386     this.buttons = [];
25387     this.allItems = [];
25388     this.addEvents({
25389         /**
25390          * @event clientvalidation
25391          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25392          * @param {Form} this
25393          * @param {Boolean} valid true if the form has passed client-side validation
25394          */
25395         clientvalidation: true,
25396         /**
25397          * @event rendered
25398          * Fires when the form is rendered
25399          * @param {Roo.form.Form} form
25400          */
25401         rendered : true
25402     });
25403     
25404     if (this.progressUrl) {
25405             // push a hidden field onto the list of fields..
25406             this.addxtype( {
25407                     xns: Roo.form, 
25408                     xtype : 'Hidden', 
25409                     name : 'UPLOAD_IDENTIFIER' 
25410             });
25411         }
25412         
25413     
25414     Roo.each(xitems, this.addxtype, this);
25415     
25416 };
25417
25418 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25419      /**
25420      * @cfg {Roo.Button} buttons[] buttons at bottom of form
25421      */
25422     
25423     /**
25424      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25425      */
25426     /**
25427      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25428      */
25429     /**
25430      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25431      */
25432     buttonAlign:'center',
25433
25434     /**
25435      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25436      */
25437     minButtonWidth:75,
25438
25439     /**
25440      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25441      * This property cascades to child containers if not set.
25442      */
25443     labelAlign:'left',
25444
25445     /**
25446      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25447      * fires a looping event with that state. This is required to bind buttons to the valid
25448      * state using the config value formBind:true on the button.
25449      */
25450     monitorValid : false,
25451
25452     /**
25453      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25454      */
25455     monitorPoll : 200,
25456     
25457     /**
25458      * @cfg {String} progressUrl - Url to return progress data 
25459      */
25460     
25461     progressUrl : false,
25462     /**
25463      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25464      * sending a formdata with extra parameters - eg uploaded elements.
25465      */
25466     
25467     formData : false,
25468     
25469     /**
25470      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25471      * fields are added and the column is closed. If no fields are passed the column remains open
25472      * until end() is called.
25473      * @param {Object} config The config to pass to the column
25474      * @param {Field} field1 (optional)
25475      * @param {Field} field2 (optional)
25476      * @param {Field} etc (optional)
25477      * @return Column The column container object
25478      */
25479     column : function(c){
25480         var col = new Roo.form.Column(c);
25481         this.start(col);
25482         if(arguments.length > 1){ // duplicate code required because of Opera
25483             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25484             this.end();
25485         }
25486         return col;
25487     },
25488
25489     /**
25490      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25491      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25492      * until end() is called.
25493      * @param {Object} config The config to pass to the fieldset
25494      * @param {Field} field1 (optional)
25495      * @param {Field} field2 (optional)
25496      * @param {Field} etc (optional)
25497      * @return FieldSet The fieldset container object
25498      */
25499     fieldset : function(c){
25500         var fs = new Roo.form.FieldSet(c);
25501         this.start(fs);
25502         if(arguments.length > 1){ // duplicate code required because of Opera
25503             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25504             this.end();
25505         }
25506         return fs;
25507     },
25508
25509     /**
25510      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25511      * fields are added and the container is closed. If no fields are passed the container remains open
25512      * until end() is called.
25513      * @param {Object} config The config to pass to the Layout
25514      * @param {Field} field1 (optional)
25515      * @param {Field} field2 (optional)
25516      * @param {Field} etc (optional)
25517      * @return Layout The container object
25518      */
25519     container : function(c){
25520         var l = new Roo.form.Layout(c);
25521         this.start(l);
25522         if(arguments.length > 1){ // duplicate code required because of Opera
25523             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25524             this.end();
25525         }
25526         return l;
25527     },
25528
25529     /**
25530      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25531      * @param {Object} container A Roo.form.Layout or subclass of Layout
25532      * @return {Form} this
25533      */
25534     start : function(c){
25535         // cascade label info
25536         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25537         this.active.stack.push(c);
25538         c.ownerCt = this.active;
25539         this.active = c;
25540         return this;
25541     },
25542
25543     /**
25544      * Closes the current open container
25545      * @return {Form} this
25546      */
25547     end : function(){
25548         if(this.active == this.root){
25549             return this;
25550         }
25551         this.active = this.active.ownerCt;
25552         return this;
25553     },
25554
25555     /**
25556      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25557      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25558      * as the label of the field.
25559      * @param {Field} field1
25560      * @param {Field} field2 (optional)
25561      * @param {Field} etc. (optional)
25562      * @return {Form} this
25563      */
25564     add : function(){
25565         this.active.stack.push.apply(this.active.stack, arguments);
25566         this.allItems.push.apply(this.allItems,arguments);
25567         var r = [];
25568         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25569             if(a[i].isFormField){
25570                 r.push(a[i]);
25571             }
25572         }
25573         if(r.length > 0){
25574             Roo.form.Form.superclass.add.apply(this, r);
25575         }
25576         return this;
25577     },
25578     
25579
25580     
25581     
25582     
25583      /**
25584      * Find any element that has been added to a form, using it's ID or name
25585      * This can include framesets, columns etc. along with regular fields..
25586      * @param {String} id - id or name to find.
25587      
25588      * @return {Element} e - or false if nothing found.
25589      */
25590     findbyId : function(id)
25591     {
25592         var ret = false;
25593         if (!id) {
25594             return ret;
25595         }
25596         Roo.each(this.allItems, function(f){
25597             if (f.id == id || f.name == id ){
25598                 ret = f;
25599                 return false;
25600             }
25601         });
25602         return ret;
25603     },
25604
25605     
25606     
25607     /**
25608      * Render this form into the passed container. This should only be called once!
25609      * @param {String/HTMLElement/Element} container The element this component should be rendered into
25610      * @return {Form} this
25611      */
25612     render : function(ct)
25613     {
25614         
25615         
25616         
25617         ct = Roo.get(ct);
25618         var o = this.autoCreate || {
25619             tag: 'form',
25620             method : this.method || 'POST',
25621             id : this.id || Roo.id()
25622         };
25623         this.initEl(ct.createChild(o));
25624
25625         this.root.render(this.el);
25626         
25627        
25628              
25629         this.items.each(function(f){
25630             f.render('x-form-el-'+f.id);
25631         });
25632
25633         if(this.buttons.length > 0){
25634             // tables are required to maintain order and for correct IE layout
25635             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25636                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25637                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25638             }}, null, true);
25639             var tr = tb.getElementsByTagName('tr')[0];
25640             for(var i = 0, len = this.buttons.length; i < len; i++) {
25641                 var b = this.buttons[i];
25642                 var td = document.createElement('td');
25643                 td.className = 'x-form-btn-td';
25644                 b.render(tr.appendChild(td));
25645             }
25646         }
25647         if(this.monitorValid){ // initialize after render
25648             this.startMonitoring();
25649         }
25650         this.fireEvent('rendered', this);
25651         return this;
25652     },
25653
25654     /**
25655      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25656      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25657      * object or a valid Roo.DomHelper element config
25658      * @param {Function} handler The function called when the button is clicked
25659      * @param {Object} scope (optional) The scope of the handler function
25660      * @return {Roo.Button}
25661      */
25662     addButton : function(config, handler, scope){
25663         var bc = {
25664             handler: handler,
25665             scope: scope,
25666             minWidth: this.minButtonWidth,
25667             hideParent:true
25668         };
25669         if(typeof config == "string"){
25670             bc.text = config;
25671         }else{
25672             Roo.apply(bc, config);
25673         }
25674         var btn = new Roo.Button(null, bc);
25675         this.buttons.push(btn);
25676         return btn;
25677     },
25678
25679      /**
25680      * Adds a series of form elements (using the xtype property as the factory method.
25681      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25682      * @param {Object} config 
25683      */
25684     
25685     addxtype : function()
25686     {
25687         var ar = Array.prototype.slice.call(arguments, 0);
25688         var ret = false;
25689         for(var i = 0; i < ar.length; i++) {
25690             if (!ar[i]) {
25691                 continue; // skip -- if this happends something invalid got sent, we 
25692                 // should ignore it, as basically that interface element will not show up
25693                 // and that should be pretty obvious!!
25694             }
25695             
25696             if (Roo.form[ar[i].xtype]) {
25697                 ar[i].form = this;
25698                 var fe = Roo.factory(ar[i], Roo.form);
25699                 if (!ret) {
25700                     ret = fe;
25701                 }
25702                 fe.form = this;
25703                 if (fe.store) {
25704                     fe.store.form = this;
25705                 }
25706                 if (fe.isLayout) {  
25707                          
25708                     this.start(fe);
25709                     this.allItems.push(fe);
25710                     if (fe.items && fe.addxtype) {
25711                         fe.addxtype.apply(fe, fe.items);
25712                         delete fe.items;
25713                     }
25714                      this.end();
25715                     continue;
25716                 }
25717                 
25718                 
25719                  
25720                 this.add(fe);
25721               //  console.log('adding ' + ar[i].xtype);
25722             }
25723             if (ar[i].xtype == 'Button') {  
25724                 //console.log('adding button');
25725                 //console.log(ar[i]);
25726                 this.addButton(ar[i]);
25727                 this.allItems.push(fe);
25728                 continue;
25729             }
25730             
25731             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25732                 alert('end is not supported on xtype any more, use items');
25733             //    this.end();
25734             //    //console.log('adding end');
25735             }
25736             
25737         }
25738         return ret;
25739     },
25740     
25741     /**
25742      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
25743      * option "monitorValid"
25744      */
25745     startMonitoring : function(){
25746         if(!this.bound){
25747             this.bound = true;
25748             Roo.TaskMgr.start({
25749                 run : this.bindHandler,
25750                 interval : this.monitorPoll || 200,
25751                 scope: this
25752             });
25753         }
25754     },
25755
25756     /**
25757      * Stops monitoring of the valid state of this form
25758      */
25759     stopMonitoring : function(){
25760         this.bound = false;
25761     },
25762
25763     // private
25764     bindHandler : function(){
25765         if(!this.bound){
25766             return false; // stops binding
25767         }
25768         var valid = true;
25769         this.items.each(function(f){
25770             if(!f.isValid(true)){
25771                 valid = false;
25772                 return false;
25773             }
25774         });
25775         for(var i = 0, len = this.buttons.length; i < len; i++){
25776             var btn = this.buttons[i];
25777             if(btn.formBind === true && btn.disabled === valid){
25778                 btn.setDisabled(!valid);
25779             }
25780         }
25781         this.fireEvent('clientvalidation', this, valid);
25782     }
25783     
25784     
25785     
25786     
25787     
25788     
25789     
25790     
25791 });
25792
25793
25794 // back compat
25795 Roo.Form = Roo.form.Form;
25796 /*
25797  * Based on:
25798  * Ext JS Library 1.1.1
25799  * Copyright(c) 2006-2007, Ext JS, LLC.
25800  *
25801  * Originally Released Under LGPL - original licence link has changed is not relivant.
25802  *
25803  * Fork - LGPL
25804  * <script type="text/javascript">
25805  */
25806
25807 // as we use this in bootstrap.
25808 Roo.namespace('Roo.form');
25809  /**
25810  * @class Roo.form.Action
25811  * Internal Class used to handle form actions
25812  * @constructor
25813  * @param {Roo.form.BasicForm} el The form element or its id
25814  * @param {Object} config Configuration options
25815  */
25816
25817  
25818  
25819 // define the action interface
25820 Roo.form.Action = function(form, options){
25821     this.form = form;
25822     this.options = options || {};
25823 };
25824 /**
25825  * Client Validation Failed
25826  * @const 
25827  */
25828 Roo.form.Action.CLIENT_INVALID = 'client';
25829 /**
25830  * Server Validation Failed
25831  * @const 
25832  */
25833 Roo.form.Action.SERVER_INVALID = 'server';
25834  /**
25835  * Connect to Server Failed
25836  * @const 
25837  */
25838 Roo.form.Action.CONNECT_FAILURE = 'connect';
25839 /**
25840  * Reading Data from Server Failed
25841  * @const 
25842  */
25843 Roo.form.Action.LOAD_FAILURE = 'load';
25844
25845 Roo.form.Action.prototype = {
25846     type : 'default',
25847     failureType : undefined,
25848     response : undefined,
25849     result : undefined,
25850
25851     // interface method
25852     run : function(options){
25853
25854     },
25855
25856     // interface method
25857     success : function(response){
25858
25859     },
25860
25861     // interface method
25862     handleResponse : function(response){
25863
25864     },
25865
25866     // default connection failure
25867     failure : function(response){
25868         
25869         this.response = response;
25870         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25871         this.form.afterAction(this, false);
25872     },
25873
25874     processResponse : function(response){
25875         this.response = response;
25876         if(!response.responseText){
25877             return true;
25878         }
25879         this.result = this.handleResponse(response);
25880         return this.result;
25881     },
25882
25883     // utility functions used internally
25884     getUrl : function(appendParams){
25885         var url = this.options.url || this.form.url || this.form.el.dom.action;
25886         if(appendParams){
25887             var p = this.getParams();
25888             if(p){
25889                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
25890             }
25891         }
25892         return url;
25893     },
25894
25895     getMethod : function(){
25896         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
25897     },
25898
25899     getParams : function(){
25900         var bp = this.form.baseParams;
25901         var p = this.options.params;
25902         if(p){
25903             if(typeof p == "object"){
25904                 p = Roo.urlEncode(Roo.applyIf(p, bp));
25905             }else if(typeof p == 'string' && bp){
25906                 p += '&' + Roo.urlEncode(bp);
25907             }
25908         }else if(bp){
25909             p = Roo.urlEncode(bp);
25910         }
25911         return p;
25912     },
25913
25914     createCallback : function(){
25915         return {
25916             success: this.success,
25917             failure: this.failure,
25918             scope: this,
25919             timeout: (this.form.timeout*1000),
25920             upload: this.form.fileUpload ? this.success : undefined
25921         };
25922     }
25923 };
25924
25925 Roo.form.Action.Submit = function(form, options){
25926     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
25927 };
25928
25929 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
25930     type : 'submit',
25931
25932     haveProgress : false,
25933     uploadComplete : false,
25934     
25935     // uploadProgress indicator.
25936     uploadProgress : function()
25937     {
25938         if (!this.form.progressUrl) {
25939             return;
25940         }
25941         
25942         if (!this.haveProgress) {
25943             Roo.MessageBox.progress("Uploading", "Uploading");
25944         }
25945         if (this.uploadComplete) {
25946            Roo.MessageBox.hide();
25947            return;
25948         }
25949         
25950         this.haveProgress = true;
25951    
25952         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
25953         
25954         var c = new Roo.data.Connection();
25955         c.request({
25956             url : this.form.progressUrl,
25957             params: {
25958                 id : uid
25959             },
25960             method: 'GET',
25961             success : function(req){
25962                //console.log(data);
25963                 var rdata = false;
25964                 var edata;
25965                 try  {
25966                    rdata = Roo.decode(req.responseText)
25967                 } catch (e) {
25968                     Roo.log("Invalid data from server..");
25969                     Roo.log(edata);
25970                     return;
25971                 }
25972                 if (!rdata || !rdata.success) {
25973                     Roo.log(rdata);
25974                     Roo.MessageBox.alert(Roo.encode(rdata));
25975                     return;
25976                 }
25977                 var data = rdata.data;
25978                 
25979                 if (this.uploadComplete) {
25980                    Roo.MessageBox.hide();
25981                    return;
25982                 }
25983                    
25984                 if (data){
25985                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
25986                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
25987                     );
25988                 }
25989                 this.uploadProgress.defer(2000,this);
25990             },
25991        
25992             failure: function(data) {
25993                 Roo.log('progress url failed ');
25994                 Roo.log(data);
25995             },
25996             scope : this
25997         });
25998            
25999     },
26000     
26001     
26002     run : function()
26003     {
26004         // run get Values on the form, so it syncs any secondary forms.
26005         this.form.getValues();
26006         
26007         var o = this.options;
26008         var method = this.getMethod();
26009         var isPost = method == 'POST';
26010         if(o.clientValidation === false || this.form.isValid()){
26011             
26012             if (this.form.progressUrl) {
26013                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
26014                     (new Date() * 1) + '' + Math.random());
26015                     
26016             } 
26017             
26018             
26019             Roo.Ajax.request(Roo.apply(this.createCallback(), {
26020                 form:this.form.el.dom,
26021                 url:this.getUrl(!isPost),
26022                 method: method,
26023                 params:isPost ? this.getParams() : null,
26024                 isUpload: this.form.fileUpload,
26025                 formData : this.form.formData
26026             }));
26027             
26028             this.uploadProgress();
26029
26030         }else if (o.clientValidation !== false){ // client validation failed
26031             this.failureType = Roo.form.Action.CLIENT_INVALID;
26032             this.form.afterAction(this, false);
26033         }
26034     },
26035
26036     success : function(response)
26037     {
26038         this.uploadComplete= true;
26039         if (this.haveProgress) {
26040             Roo.MessageBox.hide();
26041         }
26042         
26043         
26044         var result = this.processResponse(response);
26045         if(result === true || result.success){
26046             this.form.afterAction(this, true);
26047             return;
26048         }
26049         if(result.errors){
26050             this.form.markInvalid(result.errors);
26051             this.failureType = Roo.form.Action.SERVER_INVALID;
26052         }
26053         this.form.afterAction(this, false);
26054     },
26055     failure : function(response)
26056     {
26057         this.uploadComplete= true;
26058         if (this.haveProgress) {
26059             Roo.MessageBox.hide();
26060         }
26061         
26062         this.response = response;
26063         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26064         this.form.afterAction(this, false);
26065     },
26066     
26067     handleResponse : function(response){
26068         if(this.form.errorReader){
26069             var rs = this.form.errorReader.read(response);
26070             var errors = [];
26071             if(rs.records){
26072                 for(var i = 0, len = rs.records.length; i < len; i++) {
26073                     var r = rs.records[i];
26074                     errors[i] = r.data;
26075                 }
26076             }
26077             if(errors.length < 1){
26078                 errors = null;
26079             }
26080             return {
26081                 success : rs.success,
26082                 errors : errors
26083             };
26084         }
26085         var ret = false;
26086         try {
26087             ret = Roo.decode(response.responseText);
26088         } catch (e) {
26089             ret = {
26090                 success: false,
26091                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
26092                 errors : []
26093             };
26094         }
26095         return ret;
26096         
26097     }
26098 });
26099
26100
26101 Roo.form.Action.Load = function(form, options){
26102     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
26103     this.reader = this.form.reader;
26104 };
26105
26106 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
26107     type : 'load',
26108
26109     run : function(){
26110         
26111         Roo.Ajax.request(Roo.apply(
26112                 this.createCallback(), {
26113                     method:this.getMethod(),
26114                     url:this.getUrl(false),
26115                     params:this.getParams()
26116         }));
26117     },
26118
26119     success : function(response){
26120         
26121         var result = this.processResponse(response);
26122         if(result === true || !result.success || !result.data){
26123             this.failureType = Roo.form.Action.LOAD_FAILURE;
26124             this.form.afterAction(this, false);
26125             return;
26126         }
26127         this.form.clearInvalid();
26128         this.form.setValues(result.data);
26129         this.form.afterAction(this, true);
26130     },
26131
26132     handleResponse : function(response){
26133         if(this.form.reader){
26134             var rs = this.form.reader.read(response);
26135             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
26136             return {
26137                 success : rs.success,
26138                 data : data
26139             };
26140         }
26141         return Roo.decode(response.responseText);
26142     }
26143 });
26144
26145 Roo.form.Action.ACTION_TYPES = {
26146     'load' : Roo.form.Action.Load,
26147     'submit' : Roo.form.Action.Submit
26148 };/*
26149  * Based on:
26150  * Ext JS Library 1.1.1
26151  * Copyright(c) 2006-2007, Ext JS, LLC.
26152  *
26153  * Originally Released Under LGPL - original licence link has changed is not relivant.
26154  *
26155  * Fork - LGPL
26156  * <script type="text/javascript">
26157  */
26158  
26159 /**
26160  * @class Roo.form.Layout
26161  * @extends Roo.Component
26162  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26163  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
26164  * @constructor
26165  * @param {Object} config Configuration options
26166  */
26167 Roo.form.Layout = function(config){
26168     var xitems = [];
26169     if (config.items) {
26170         xitems = config.items;
26171         delete config.items;
26172     }
26173     Roo.form.Layout.superclass.constructor.call(this, config);
26174     this.stack = [];
26175     Roo.each(xitems, this.addxtype, this);
26176      
26177 };
26178
26179 Roo.extend(Roo.form.Layout, Roo.Component, {
26180     /**
26181      * @cfg {String/Object} autoCreate
26182      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
26183      */
26184     /**
26185      * @cfg {String/Object/Function} style
26186      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
26187      * a function which returns such a specification.
26188      */
26189     /**
26190      * @cfg {String} labelAlign
26191      * Valid values are "left," "top" and "right" (defaults to "left")
26192      */
26193     /**
26194      * @cfg {Number} labelWidth
26195      * Fixed width in pixels of all field labels (defaults to undefined)
26196      */
26197     /**
26198      * @cfg {Boolean} clear
26199      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
26200      */
26201     clear : true,
26202     /**
26203      * @cfg {String} labelSeparator
26204      * The separator to use after field labels (defaults to ':')
26205      */
26206     labelSeparator : ':',
26207     /**
26208      * @cfg {Boolean} hideLabels
26209      * True to suppress the display of field labels in this layout (defaults to false)
26210      */
26211     hideLabels : false,
26212
26213     // private
26214     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
26215     
26216     isLayout : true,
26217     
26218     // private
26219     onRender : function(ct, position){
26220         if(this.el){ // from markup
26221             this.el = Roo.get(this.el);
26222         }else {  // generate
26223             var cfg = this.getAutoCreate();
26224             this.el = ct.createChild(cfg, position);
26225         }
26226         if(this.style){
26227             this.el.applyStyles(this.style);
26228         }
26229         if(this.labelAlign){
26230             this.el.addClass('x-form-label-'+this.labelAlign);
26231         }
26232         if(this.hideLabels){
26233             this.labelStyle = "display:none";
26234             this.elementStyle = "padding-left:0;";
26235         }else{
26236             if(typeof this.labelWidth == 'number'){
26237                 this.labelStyle = "width:"+this.labelWidth+"px;";
26238                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
26239             }
26240             if(this.labelAlign == 'top'){
26241                 this.labelStyle = "width:auto;";
26242                 this.elementStyle = "padding-left:0;";
26243             }
26244         }
26245         var stack = this.stack;
26246         var slen = stack.length;
26247         if(slen > 0){
26248             if(!this.fieldTpl){
26249                 var t = new Roo.Template(
26250                     '<div class="x-form-item {5}">',
26251                         '<label for="{0}" style="{2}">{1}{4}</label>',
26252                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26253                         '</div>',
26254                     '</div><div class="x-form-clear-left"></div>'
26255                 );
26256                 t.disableFormats = true;
26257                 t.compile();
26258                 Roo.form.Layout.prototype.fieldTpl = t;
26259             }
26260             for(var i = 0; i < slen; i++) {
26261                 if(stack[i].isFormField){
26262                     this.renderField(stack[i]);
26263                 }else{
26264                     this.renderComponent(stack[i]);
26265                 }
26266             }
26267         }
26268         if(this.clear){
26269             this.el.createChild({cls:'x-form-clear'});
26270         }
26271     },
26272
26273     // private
26274     renderField : function(f){
26275         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
26276                f.id, //0
26277                f.fieldLabel, //1
26278                f.labelStyle||this.labelStyle||'', //2
26279                this.elementStyle||'', //3
26280                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
26281                f.itemCls||this.itemCls||''  //5
26282        ], true).getPrevSibling());
26283     },
26284
26285     // private
26286     renderComponent : function(c){
26287         c.render(c.isLayout ? this.el : this.el.createChild());    
26288     },
26289     /**
26290      * Adds a object form elements (using the xtype property as the factory method.)
26291      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
26292      * @param {Object} config 
26293      */
26294     addxtype : function(o)
26295     {
26296         // create the lement.
26297         o.form = this.form;
26298         var fe = Roo.factory(o, Roo.form);
26299         this.form.allItems.push(fe);
26300         this.stack.push(fe);
26301         
26302         if (fe.isFormField) {
26303             this.form.items.add(fe);
26304         }
26305          
26306         return fe;
26307     }
26308 });
26309
26310 /**
26311  * @class Roo.form.Column
26312  * @extends Roo.form.Layout
26313  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
26314  * @constructor
26315  * @param {Object} config Configuration options
26316  */
26317 Roo.form.Column = function(config){
26318     Roo.form.Column.superclass.constructor.call(this, config);
26319 };
26320
26321 Roo.extend(Roo.form.Column, Roo.form.Layout, {
26322     /**
26323      * @cfg {Number/String} width
26324      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26325      */
26326     /**
26327      * @cfg {String/Object} autoCreate
26328      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
26329      */
26330
26331     // private
26332     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
26333
26334     // private
26335     onRender : function(ct, position){
26336         Roo.form.Column.superclass.onRender.call(this, ct, position);
26337         if(this.width){
26338             this.el.setWidth(this.width);
26339         }
26340     }
26341 });
26342
26343
26344 /**
26345  * @class Roo.form.Row
26346  * @extends Roo.form.Layout
26347  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26348  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
26349  * @constructor
26350  * @param {Object} config Configuration options
26351  */
26352
26353  
26354 Roo.form.Row = function(config){
26355     Roo.form.Row.superclass.constructor.call(this, config);
26356 };
26357  
26358 Roo.extend(Roo.form.Row, Roo.form.Layout, {
26359       /**
26360      * @cfg {Number/String} width
26361      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26362      */
26363     /**
26364      * @cfg {Number/String} height
26365      * The fixed height of the column in pixels or CSS value (defaults to "auto")
26366      */
26367     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
26368     
26369     padWidth : 20,
26370     // private
26371     onRender : function(ct, position){
26372         //console.log('row render');
26373         if(!this.rowTpl){
26374             var t = new Roo.Template(
26375                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
26376                     '<label for="{0}" style="{2}">{1}{4}</label>',
26377                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26378                     '</div>',
26379                 '</div>'
26380             );
26381             t.disableFormats = true;
26382             t.compile();
26383             Roo.form.Layout.prototype.rowTpl = t;
26384         }
26385         this.fieldTpl = this.rowTpl;
26386         
26387         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26388         var labelWidth = 100;
26389         
26390         if ((this.labelAlign != 'top')) {
26391             if (typeof this.labelWidth == 'number') {
26392                 labelWidth = this.labelWidth
26393             }
26394             this.padWidth =  20 + labelWidth;
26395             
26396         }
26397         
26398         Roo.form.Column.superclass.onRender.call(this, ct, position);
26399         if(this.width){
26400             this.el.setWidth(this.width);
26401         }
26402         if(this.height){
26403             this.el.setHeight(this.height);
26404         }
26405     },
26406     
26407     // private
26408     renderField : function(f){
26409         f.fieldEl = this.fieldTpl.append(this.el, [
26410                f.id, f.fieldLabel,
26411                f.labelStyle||this.labelStyle||'',
26412                this.elementStyle||'',
26413                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26414                f.itemCls||this.itemCls||'',
26415                f.width ? f.width + this.padWidth : 160 + this.padWidth
26416        ],true);
26417     }
26418 });
26419  
26420
26421 /**
26422  * @class Roo.form.FieldSet
26423  * @extends Roo.form.Layout
26424  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26425  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26426  * @constructor
26427  * @param {Object} config Configuration options
26428  */
26429 Roo.form.FieldSet = function(config){
26430     Roo.form.FieldSet.superclass.constructor.call(this, config);
26431 };
26432
26433 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26434     /**
26435      * @cfg {String} legend
26436      * The text to display as the legend for the FieldSet (defaults to '')
26437      */
26438     /**
26439      * @cfg {String/Object} autoCreate
26440      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26441      */
26442
26443     // private
26444     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26445
26446     // private
26447     onRender : function(ct, position){
26448         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26449         if(this.legend){
26450             this.setLegend(this.legend);
26451         }
26452     },
26453
26454     // private
26455     setLegend : function(text){
26456         if(this.rendered){
26457             this.el.child('legend').update(text);
26458         }
26459     }
26460 });/*
26461  * Based on:
26462  * Ext JS Library 1.1.1
26463  * Copyright(c) 2006-2007, Ext JS, LLC.
26464  *
26465  * Originally Released Under LGPL - original licence link has changed is not relivant.
26466  *
26467  * Fork - LGPL
26468  * <script type="text/javascript">
26469  */
26470 /**
26471  * @class Roo.form.VTypes
26472  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26473  * @singleton
26474  */
26475 Roo.form.VTypes = function(){
26476     // closure these in so they are only created once.
26477     var alpha = /^[a-zA-Z_]+$/;
26478     var alphanum = /^[a-zA-Z0-9_]+$/;
26479     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26480     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26481
26482     // All these messages and functions are configurable
26483     return {
26484         /**
26485          * The function used to validate email addresses
26486          * @param {String} value The email address
26487          */
26488         'email' : function(v){
26489             return email.test(v);
26490         },
26491         /**
26492          * The error text to display when the email validation function returns false
26493          * @type String
26494          */
26495         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26496         /**
26497          * The keystroke filter mask to be applied on email input
26498          * @type RegExp
26499          */
26500         'emailMask' : /[a-z0-9_\.\-@]/i,
26501
26502         /**
26503          * The function used to validate URLs
26504          * @param {String} value The URL
26505          */
26506         'url' : function(v){
26507             return url.test(v);
26508         },
26509         /**
26510          * The error text to display when the url validation function returns false
26511          * @type String
26512          */
26513         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26514         
26515         /**
26516          * The function used to validate alpha values
26517          * @param {String} value The value
26518          */
26519         'alpha' : function(v){
26520             return alpha.test(v);
26521         },
26522         /**
26523          * The error text to display when the alpha validation function returns false
26524          * @type String
26525          */
26526         'alphaText' : 'This field should only contain letters and _',
26527         /**
26528          * The keystroke filter mask to be applied on alpha input
26529          * @type RegExp
26530          */
26531         'alphaMask' : /[a-z_]/i,
26532
26533         /**
26534          * The function used to validate alphanumeric values
26535          * @param {String} value The value
26536          */
26537         'alphanum' : function(v){
26538             return alphanum.test(v);
26539         },
26540         /**
26541          * The error text to display when the alphanumeric validation function returns false
26542          * @type String
26543          */
26544         'alphanumText' : 'This field should only contain letters, numbers and _',
26545         /**
26546          * The keystroke filter mask to be applied on alphanumeric input
26547          * @type RegExp
26548          */
26549         'alphanumMask' : /[a-z0-9_]/i
26550     };
26551 }();//<script type="text/javascript">
26552
26553 /**
26554  * @class Roo.form.FCKeditor
26555  * @extends Roo.form.TextArea
26556  * Wrapper around the FCKEditor http://www.fckeditor.net
26557  * @constructor
26558  * Creates a new FCKeditor
26559  * @param {Object} config Configuration options
26560  */
26561 Roo.form.FCKeditor = function(config){
26562     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26563     this.addEvents({
26564          /**
26565          * @event editorinit
26566          * Fired when the editor is initialized - you can add extra handlers here..
26567          * @param {FCKeditor} this
26568          * @param {Object} the FCK object.
26569          */
26570         editorinit : true
26571     });
26572     
26573     
26574 };
26575 Roo.form.FCKeditor.editors = { };
26576 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26577 {
26578     //defaultAutoCreate : {
26579     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26580     //},
26581     // private
26582     /**
26583      * @cfg {Object} fck options - see fck manual for details.
26584      */
26585     fckconfig : false,
26586     
26587     /**
26588      * @cfg {Object} fck toolbar set (Basic or Default)
26589      */
26590     toolbarSet : 'Basic',
26591     /**
26592      * @cfg {Object} fck BasePath
26593      */ 
26594     basePath : '/fckeditor/',
26595     
26596     
26597     frame : false,
26598     
26599     value : '',
26600     
26601    
26602     onRender : function(ct, position)
26603     {
26604         if(!this.el){
26605             this.defaultAutoCreate = {
26606                 tag: "textarea",
26607                 style:"width:300px;height:60px;",
26608                 autocomplete: "new-password"
26609             };
26610         }
26611         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26612         /*
26613         if(this.grow){
26614             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26615             if(this.preventScrollbars){
26616                 this.el.setStyle("overflow", "hidden");
26617             }
26618             this.el.setHeight(this.growMin);
26619         }
26620         */
26621         //console.log('onrender' + this.getId() );
26622         Roo.form.FCKeditor.editors[this.getId()] = this;
26623          
26624
26625         this.replaceTextarea() ;
26626         
26627     },
26628     
26629     getEditor : function() {
26630         return this.fckEditor;
26631     },
26632     /**
26633      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26634      * @param {Mixed} value The value to set
26635      */
26636     
26637     
26638     setValue : function(value)
26639     {
26640         //console.log('setValue: ' + value);
26641         
26642         if(typeof(value) == 'undefined') { // not sure why this is happending...
26643             return;
26644         }
26645         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26646         
26647         //if(!this.el || !this.getEditor()) {
26648         //    this.value = value;
26649             //this.setValue.defer(100,this,[value]);    
26650         //    return;
26651         //} 
26652         
26653         if(!this.getEditor()) {
26654             return;
26655         }
26656         
26657         this.getEditor().SetData(value);
26658         
26659         //
26660
26661     },
26662
26663     /**
26664      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26665      * @return {Mixed} value The field value
26666      */
26667     getValue : function()
26668     {
26669         
26670         if (this.frame && this.frame.dom.style.display == 'none') {
26671             return Roo.form.FCKeditor.superclass.getValue.call(this);
26672         }
26673         
26674         if(!this.el || !this.getEditor()) {
26675            
26676            // this.getValue.defer(100,this); 
26677             return this.value;
26678         }
26679        
26680         
26681         var value=this.getEditor().GetData();
26682         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26683         return Roo.form.FCKeditor.superclass.getValue.call(this);
26684         
26685
26686     },
26687
26688     /**
26689      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26690      * @return {Mixed} value The field value
26691      */
26692     getRawValue : function()
26693     {
26694         if (this.frame && this.frame.dom.style.display == 'none') {
26695             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26696         }
26697         
26698         if(!this.el || !this.getEditor()) {
26699             //this.getRawValue.defer(100,this); 
26700             return this.value;
26701             return;
26702         }
26703         
26704         
26705         
26706         var value=this.getEditor().GetData();
26707         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26708         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26709          
26710     },
26711     
26712     setSize : function(w,h) {
26713         
26714         
26715         
26716         //if (this.frame && this.frame.dom.style.display == 'none') {
26717         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26718         //    return;
26719         //}
26720         //if(!this.el || !this.getEditor()) {
26721         //    this.setSize.defer(100,this, [w,h]); 
26722         //    return;
26723         //}
26724         
26725         
26726         
26727         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26728         
26729         this.frame.dom.setAttribute('width', w);
26730         this.frame.dom.setAttribute('height', h);
26731         this.frame.setSize(w,h);
26732         
26733     },
26734     
26735     toggleSourceEdit : function(value) {
26736         
26737       
26738          
26739         this.el.dom.style.display = value ? '' : 'none';
26740         this.frame.dom.style.display = value ?  'none' : '';
26741         
26742     },
26743     
26744     
26745     focus: function(tag)
26746     {
26747         if (this.frame.dom.style.display == 'none') {
26748             return Roo.form.FCKeditor.superclass.focus.call(this);
26749         }
26750         if(!this.el || !this.getEditor()) {
26751             this.focus.defer(100,this, [tag]); 
26752             return;
26753         }
26754         
26755         
26756         
26757         
26758         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
26759         this.getEditor().Focus();
26760         if (tgs.length) {
26761             if (!this.getEditor().Selection.GetSelection()) {
26762                 this.focus.defer(100,this, [tag]); 
26763                 return;
26764             }
26765             
26766             
26767             var r = this.getEditor().EditorDocument.createRange();
26768             r.setStart(tgs[0],0);
26769             r.setEnd(tgs[0],0);
26770             this.getEditor().Selection.GetSelection().removeAllRanges();
26771             this.getEditor().Selection.GetSelection().addRange(r);
26772             this.getEditor().Focus();
26773         }
26774         
26775     },
26776     
26777     
26778     
26779     replaceTextarea : function()
26780     {
26781         if ( document.getElementById( this.getId() + '___Frame' ) ) {
26782             return ;
26783         }
26784         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
26785         //{
26786             // We must check the elements firstly using the Id and then the name.
26787         var oTextarea = document.getElementById( this.getId() );
26788         
26789         var colElementsByName = document.getElementsByName( this.getId() ) ;
26790          
26791         oTextarea.style.display = 'none' ;
26792
26793         if ( oTextarea.tabIndex ) {            
26794             this.TabIndex = oTextarea.tabIndex ;
26795         }
26796         
26797         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
26798         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
26799         this.frame = Roo.get(this.getId() + '___Frame')
26800     },
26801     
26802     _getConfigHtml : function()
26803     {
26804         var sConfig = '' ;
26805
26806         for ( var o in this.fckconfig ) {
26807             sConfig += sConfig.length > 0  ? '&amp;' : '';
26808             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
26809         }
26810
26811         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
26812     },
26813     
26814     
26815     _getIFrameHtml : function()
26816     {
26817         var sFile = 'fckeditor.html' ;
26818         /* no idea what this is about..
26819         try
26820         {
26821             if ( (/fcksource=true/i).test( window.top.location.search ) )
26822                 sFile = 'fckeditor.original.html' ;
26823         }
26824         catch (e) { 
26825         */
26826
26827         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
26828         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
26829         
26830         
26831         var html = '<iframe id="' + this.getId() +
26832             '___Frame" src="' + sLink +
26833             '" width="' + this.width +
26834             '" height="' + this.height + '"' +
26835             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
26836             ' frameborder="0" scrolling="no"></iframe>' ;
26837
26838         return html ;
26839     },
26840     
26841     _insertHtmlBefore : function( html, element )
26842     {
26843         if ( element.insertAdjacentHTML )       {
26844             // IE
26845             element.insertAdjacentHTML( 'beforeBegin', html ) ;
26846         } else { // Gecko
26847             var oRange = document.createRange() ;
26848             oRange.setStartBefore( element ) ;
26849             var oFragment = oRange.createContextualFragment( html );
26850             element.parentNode.insertBefore( oFragment, element ) ;
26851         }
26852     }
26853     
26854     
26855   
26856     
26857     
26858     
26859     
26860
26861 });
26862
26863 //Roo.reg('fckeditor', Roo.form.FCKeditor);
26864
26865 function FCKeditor_OnComplete(editorInstance){
26866     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
26867     f.fckEditor = editorInstance;
26868     //console.log("loaded");
26869     f.fireEvent('editorinit', f, editorInstance);
26870
26871   
26872
26873  
26874
26875
26876
26877
26878
26879
26880
26881
26882
26883
26884
26885
26886
26887
26888
26889 //<script type="text/javascript">
26890 /**
26891  * @class Roo.form.GridField
26892  * @extends Roo.form.Field
26893  * Embed a grid (or editable grid into a form)
26894  * STATUS ALPHA
26895  * 
26896  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
26897  * it needs 
26898  * xgrid.store = Roo.data.Store
26899  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
26900  * xgrid.store.reader = Roo.data.JsonReader 
26901  * 
26902  * 
26903  * @constructor
26904  * Creates a new GridField
26905  * @param {Object} config Configuration options
26906  */
26907 Roo.form.GridField = function(config){
26908     Roo.form.GridField.superclass.constructor.call(this, config);
26909      
26910 };
26911
26912 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
26913     /**
26914      * @cfg {Number} width  - used to restrict width of grid..
26915      */
26916     width : 100,
26917     /**
26918      * @cfg {Number} height - used to restrict height of grid..
26919      */
26920     height : 50,
26921      /**
26922      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
26923          * 
26924          *}
26925      */
26926     xgrid : false, 
26927     /**
26928      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26929      * {tag: "input", type: "checkbox", autocomplete: "off"})
26930      */
26931    // defaultAutoCreate : { tag: 'div' },
26932     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
26933     /**
26934      * @cfg {String} addTitle Text to include for adding a title.
26935      */
26936     addTitle : false,
26937     //
26938     onResize : function(){
26939         Roo.form.Field.superclass.onResize.apply(this, arguments);
26940     },
26941
26942     initEvents : function(){
26943         // Roo.form.Checkbox.superclass.initEvents.call(this);
26944         // has no events...
26945        
26946     },
26947
26948
26949     getResizeEl : function(){
26950         return this.wrap;
26951     },
26952
26953     getPositionEl : function(){
26954         return this.wrap;
26955     },
26956
26957     // private
26958     onRender : function(ct, position){
26959         
26960         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
26961         var style = this.style;
26962         delete this.style;
26963         
26964         Roo.form.GridField.superclass.onRender.call(this, ct, position);
26965         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
26966         this.viewEl = this.wrap.createChild({ tag: 'div' });
26967         if (style) {
26968             this.viewEl.applyStyles(style);
26969         }
26970         if (this.width) {
26971             this.viewEl.setWidth(this.width);
26972         }
26973         if (this.height) {
26974             this.viewEl.setHeight(this.height);
26975         }
26976         //if(this.inputValue !== undefined){
26977         //this.setValue(this.value);
26978         
26979         
26980         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
26981         
26982         
26983         this.grid.render();
26984         this.grid.getDataSource().on('remove', this.refreshValue, this);
26985         this.grid.getDataSource().on('update', this.refreshValue, this);
26986         this.grid.on('afteredit', this.refreshValue, this);
26987  
26988     },
26989      
26990     
26991     /**
26992      * Sets the value of the item. 
26993      * @param {String} either an object  or a string..
26994      */
26995     setValue : function(v){
26996         //this.value = v;
26997         v = v || []; // empty set..
26998         // this does not seem smart - it really only affects memoryproxy grids..
26999         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
27000             var ds = this.grid.getDataSource();
27001             // assumes a json reader..
27002             var data = {}
27003             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
27004             ds.loadData( data);
27005         }
27006         // clear selection so it does not get stale.
27007         if (this.grid.sm) { 
27008             this.grid.sm.clearSelections();
27009         }
27010         
27011         Roo.form.GridField.superclass.setValue.call(this, v);
27012         this.refreshValue();
27013         // should load data in the grid really....
27014     },
27015     
27016     // private
27017     refreshValue: function() {
27018          var val = [];
27019         this.grid.getDataSource().each(function(r) {
27020             val.push(r.data);
27021         });
27022         this.el.dom.value = Roo.encode(val);
27023     }
27024     
27025      
27026     
27027     
27028 });/*
27029  * Based on:
27030  * Ext JS Library 1.1.1
27031  * Copyright(c) 2006-2007, Ext JS, LLC.
27032  *
27033  * Originally Released Under LGPL - original licence link has changed is not relivant.
27034  *
27035  * Fork - LGPL
27036  * <script type="text/javascript">
27037  */
27038 /**
27039  * @class Roo.form.DisplayField
27040  * @extends Roo.form.Field
27041  * A generic Field to display non-editable data.
27042  * @cfg {Boolean} closable (true|false) default false
27043  * @constructor
27044  * Creates a new Display Field item.
27045  * @param {Object} config Configuration options
27046  */
27047 Roo.form.DisplayField = function(config){
27048     Roo.form.DisplayField.superclass.constructor.call(this, config);
27049     
27050     this.addEvents({
27051         /**
27052          * @event close
27053          * Fires after the click the close btn
27054              * @param {Roo.form.DisplayField} this
27055              */
27056         close : true
27057     });
27058 };
27059
27060 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
27061     inputType:      'hidden',
27062     allowBlank:     true,
27063     readOnly:         true,
27064     
27065  
27066     /**
27067      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27068      */
27069     focusClass : undefined,
27070     /**
27071      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27072      */
27073     fieldClass: 'x-form-field',
27074     
27075      /**
27076      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
27077      */
27078     valueRenderer: undefined,
27079     
27080     width: 100,
27081     /**
27082      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27083      * {tag: "input", type: "checkbox", autocomplete: "off"})
27084      */
27085      
27086  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27087  
27088     closable : false,
27089     
27090     onResize : function(){
27091         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
27092         
27093     },
27094
27095     initEvents : function(){
27096         // Roo.form.Checkbox.superclass.initEvents.call(this);
27097         // has no events...
27098         
27099         if(this.closable){
27100             this.closeEl.on('click', this.onClose, this);
27101         }
27102        
27103     },
27104
27105
27106     getResizeEl : function(){
27107         return this.wrap;
27108     },
27109
27110     getPositionEl : function(){
27111         return this.wrap;
27112     },
27113
27114     // private
27115     onRender : function(ct, position){
27116         
27117         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
27118         //if(this.inputValue !== undefined){
27119         this.wrap = this.el.wrap();
27120         
27121         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
27122         
27123         if(this.closable){
27124             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
27125         }
27126         
27127         if (this.bodyStyle) {
27128             this.viewEl.applyStyles(this.bodyStyle);
27129         }
27130         //this.viewEl.setStyle('padding', '2px');
27131         
27132         this.setValue(this.value);
27133         
27134     },
27135 /*
27136     // private
27137     initValue : Roo.emptyFn,
27138
27139   */
27140
27141         // private
27142     onClick : function(){
27143         
27144     },
27145
27146     /**
27147      * Sets the checked state of the checkbox.
27148      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
27149      */
27150     setValue : function(v){
27151         this.value = v;
27152         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
27153         // this might be called before we have a dom element..
27154         if (!this.viewEl) {
27155             return;
27156         }
27157         this.viewEl.dom.innerHTML = html;
27158         Roo.form.DisplayField.superclass.setValue.call(this, v);
27159
27160     },
27161     
27162     onClose : function(e)
27163     {
27164         e.preventDefault();
27165         
27166         this.fireEvent('close', this);
27167     }
27168 });/*
27169  * 
27170  * Licence- LGPL
27171  * 
27172  */
27173
27174 /**
27175  * @class Roo.form.DayPicker
27176  * @extends Roo.form.Field
27177  * A Day picker show [M] [T] [W] ....
27178  * @constructor
27179  * Creates a new Day Picker
27180  * @param {Object} config Configuration options
27181  */
27182 Roo.form.DayPicker= function(config){
27183     Roo.form.DayPicker.superclass.constructor.call(this, config);
27184      
27185 };
27186
27187 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
27188     /**
27189      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27190      */
27191     focusClass : undefined,
27192     /**
27193      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27194      */
27195     fieldClass: "x-form-field",
27196    
27197     /**
27198      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27199      * {tag: "input", type: "checkbox", autocomplete: "off"})
27200      */
27201     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
27202     
27203    
27204     actionMode : 'viewEl', 
27205     //
27206     // private
27207  
27208     inputType : 'hidden',
27209     
27210      
27211     inputElement: false, // real input element?
27212     basedOn: false, // ????
27213     
27214     isFormField: true, // not sure where this is needed!!!!
27215
27216     onResize : function(){
27217         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
27218         if(!this.boxLabel){
27219             this.el.alignTo(this.wrap, 'c-c');
27220         }
27221     },
27222
27223     initEvents : function(){
27224         Roo.form.Checkbox.superclass.initEvents.call(this);
27225         this.el.on("click", this.onClick,  this);
27226         this.el.on("change", this.onClick,  this);
27227     },
27228
27229
27230     getResizeEl : function(){
27231         return this.wrap;
27232     },
27233
27234     getPositionEl : function(){
27235         return this.wrap;
27236     },
27237
27238     
27239     // private
27240     onRender : function(ct, position){
27241         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
27242        
27243         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
27244         
27245         var r1 = '<table><tr>';
27246         var r2 = '<tr class="x-form-daypick-icons">';
27247         for (var i=0; i < 7; i++) {
27248             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
27249             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
27250         }
27251         
27252         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
27253         viewEl.select('img').on('click', this.onClick, this);
27254         this.viewEl = viewEl;   
27255         
27256         
27257         // this will not work on Chrome!!!
27258         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
27259         this.el.on('propertychange', this.setFromHidden,  this);  //ie
27260         
27261         
27262           
27263
27264     },
27265
27266     // private
27267     initValue : Roo.emptyFn,
27268
27269     /**
27270      * Returns the checked state of the checkbox.
27271      * @return {Boolean} True if checked, else false
27272      */
27273     getValue : function(){
27274         return this.el.dom.value;
27275         
27276     },
27277
27278         // private
27279     onClick : function(e){ 
27280         //this.setChecked(!this.checked);
27281         Roo.get(e.target).toggleClass('x-menu-item-checked');
27282         this.refreshValue();
27283         //if(this.el.dom.checked != this.checked){
27284         //    this.setValue(this.el.dom.checked);
27285        // }
27286     },
27287     
27288     // private
27289     refreshValue : function()
27290     {
27291         var val = '';
27292         this.viewEl.select('img',true).each(function(e,i,n)  {
27293             val += e.is(".x-menu-item-checked") ? String(n) : '';
27294         });
27295         this.setValue(val, true);
27296     },
27297
27298     /**
27299      * Sets the checked state of the checkbox.
27300      * On is always based on a string comparison between inputValue and the param.
27301      * @param {Boolean/String} value - the value to set 
27302      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
27303      */
27304     setValue : function(v,suppressEvent){
27305         if (!this.el.dom) {
27306             return;
27307         }
27308         var old = this.el.dom.value ;
27309         this.el.dom.value = v;
27310         if (suppressEvent) {
27311             return ;
27312         }
27313          
27314         // update display..
27315         this.viewEl.select('img',true).each(function(e,i,n)  {
27316             
27317             var on = e.is(".x-menu-item-checked");
27318             var newv = v.indexOf(String(n)) > -1;
27319             if (on != newv) {
27320                 e.toggleClass('x-menu-item-checked');
27321             }
27322             
27323         });
27324         
27325         
27326         this.fireEvent('change', this, v, old);
27327         
27328         
27329     },
27330    
27331     // handle setting of hidden value by some other method!!?!?
27332     setFromHidden: function()
27333     {
27334         if(!this.el){
27335             return;
27336         }
27337         //console.log("SET FROM HIDDEN");
27338         //alert('setFrom hidden');
27339         this.setValue(this.el.dom.value);
27340     },
27341     
27342     onDestroy : function()
27343     {
27344         if(this.viewEl){
27345             Roo.get(this.viewEl).remove();
27346         }
27347          
27348         Roo.form.DayPicker.superclass.onDestroy.call(this);
27349     }
27350
27351 });/*
27352  * RooJS Library 1.1.1
27353  * Copyright(c) 2008-2011  Alan Knowles
27354  *
27355  * License - LGPL
27356  */
27357  
27358
27359 /**
27360  * @class Roo.form.ComboCheck
27361  * @extends Roo.form.ComboBox
27362  * A combobox for multiple select items.
27363  *
27364  * FIXME - could do with a reset button..
27365  * 
27366  * @constructor
27367  * Create a new ComboCheck
27368  * @param {Object} config Configuration options
27369  */
27370 Roo.form.ComboCheck = function(config){
27371     Roo.form.ComboCheck.superclass.constructor.call(this, config);
27372     // should verify some data...
27373     // like
27374     // hiddenName = required..
27375     // displayField = required
27376     // valudField == required
27377     var req= [ 'hiddenName', 'displayField', 'valueField' ];
27378     var _t = this;
27379     Roo.each(req, function(e) {
27380         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
27381             throw "Roo.form.ComboCheck : missing value for: " + e;
27382         }
27383     });
27384     
27385     
27386 };
27387
27388 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
27389      
27390      
27391     editable : false,
27392      
27393     selectedClass: 'x-menu-item-checked', 
27394     
27395     // private
27396     onRender : function(ct, position){
27397         var _t = this;
27398         
27399         
27400         
27401         if(!this.tpl){
27402             var cls = 'x-combo-list';
27403
27404             
27405             this.tpl =  new Roo.Template({
27406                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
27407                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
27408                    '<span>{' + this.displayField + '}</span>' +
27409                     '</div>' 
27410                 
27411             });
27412         }
27413  
27414         
27415         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
27416         this.view.singleSelect = false;
27417         this.view.multiSelect = true;
27418         this.view.toggleSelect = true;
27419         this.pageTb.add(new Roo.Toolbar.Fill(), {
27420             
27421             text: 'Done',
27422             handler: function()
27423             {
27424                 _t.collapse();
27425             }
27426         });
27427     },
27428     
27429     onViewOver : function(e, t){
27430         // do nothing...
27431         return;
27432         
27433     },
27434     
27435     onViewClick : function(doFocus,index){
27436         return;
27437         
27438     },
27439     select: function () {
27440         //Roo.log("SELECT CALLED");
27441     },
27442      
27443     selectByValue : function(xv, scrollIntoView){
27444         var ar = this.getValueArray();
27445         var sels = [];
27446         
27447         Roo.each(ar, function(v) {
27448             if(v === undefined || v === null){
27449                 return;
27450             }
27451             var r = this.findRecord(this.valueField, v);
27452             if(r){
27453                 sels.push(this.store.indexOf(r))
27454                 
27455             }
27456         },this);
27457         this.view.select(sels);
27458         return false;
27459     },
27460     
27461     
27462     
27463     onSelect : function(record, index){
27464        // Roo.log("onselect Called");
27465        // this is only called by the clear button now..
27466         this.view.clearSelections();
27467         this.setValue('[]');
27468         if (this.value != this.valueBefore) {
27469             this.fireEvent('change', this, this.value, this.valueBefore);
27470             this.valueBefore = this.value;
27471         }
27472     },
27473     getValueArray : function()
27474     {
27475         var ar = [] ;
27476         
27477         try {
27478             //Roo.log(this.value);
27479             if (typeof(this.value) == 'undefined') {
27480                 return [];
27481             }
27482             var ar = Roo.decode(this.value);
27483             return  ar instanceof Array ? ar : []; //?? valid?
27484             
27485         } catch(e) {
27486             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
27487             return [];
27488         }
27489          
27490     },
27491     expand : function ()
27492     {
27493         
27494         Roo.form.ComboCheck.superclass.expand.call(this);
27495         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27496         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27497         
27498
27499     },
27500     
27501     collapse : function(){
27502         Roo.form.ComboCheck.superclass.collapse.call(this);
27503         var sl = this.view.getSelectedIndexes();
27504         var st = this.store;
27505         var nv = [];
27506         var tv = [];
27507         var r;
27508         Roo.each(sl, function(i) {
27509             r = st.getAt(i);
27510             nv.push(r.get(this.valueField));
27511         },this);
27512         this.setValue(Roo.encode(nv));
27513         if (this.value != this.valueBefore) {
27514
27515             this.fireEvent('change', this, this.value, this.valueBefore);
27516             this.valueBefore = this.value;
27517         }
27518         
27519     },
27520     
27521     setValue : function(v){
27522         // Roo.log(v);
27523         this.value = v;
27524         
27525         var vals = this.getValueArray();
27526         var tv = [];
27527         Roo.each(vals, function(k) {
27528             var r = this.findRecord(this.valueField, k);
27529             if(r){
27530                 tv.push(r.data[this.displayField]);
27531             }else if(this.valueNotFoundText !== undefined){
27532                 tv.push( this.valueNotFoundText );
27533             }
27534         },this);
27535        // Roo.log(tv);
27536         
27537         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27538         this.hiddenField.value = v;
27539         this.value = v;
27540     }
27541     
27542 });/*
27543  * Based on:
27544  * Ext JS Library 1.1.1
27545  * Copyright(c) 2006-2007, Ext JS, LLC.
27546  *
27547  * Originally Released Under LGPL - original licence link has changed is not relivant.
27548  *
27549  * Fork - LGPL
27550  * <script type="text/javascript">
27551  */
27552  
27553 /**
27554  * @class Roo.form.Signature
27555  * @extends Roo.form.Field
27556  * Signature field.  
27557  * @constructor
27558  * 
27559  * @param {Object} config Configuration options
27560  */
27561
27562 Roo.form.Signature = function(config){
27563     Roo.form.Signature.superclass.constructor.call(this, config);
27564     
27565     this.addEvents({// not in used??
27566          /**
27567          * @event confirm
27568          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27569              * @param {Roo.form.Signature} combo This combo box
27570              */
27571         'confirm' : true,
27572         /**
27573          * @event reset
27574          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27575              * @param {Roo.form.ComboBox} combo This combo box
27576              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27577              */
27578         'reset' : true
27579     });
27580 };
27581
27582 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
27583     /**
27584      * @cfg {Object} labels Label to use when rendering a form.
27585      * defaults to 
27586      * labels : { 
27587      *      clear : "Clear",
27588      *      confirm : "Confirm"
27589      *  }
27590      */
27591     labels : { 
27592         clear : "Clear",
27593         confirm : "Confirm"
27594     },
27595     /**
27596      * @cfg {Number} width The signature panel width (defaults to 300)
27597      */
27598     width: 300,
27599     /**
27600      * @cfg {Number} height The signature panel height (defaults to 100)
27601      */
27602     height : 100,
27603     /**
27604      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27605      */
27606     allowBlank : false,
27607     
27608     //private
27609     // {Object} signPanel The signature SVG panel element (defaults to {})
27610     signPanel : {},
27611     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27612     isMouseDown : false,
27613     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27614     isConfirmed : false,
27615     // {String} signatureTmp SVG mapping string (defaults to empty string)
27616     signatureTmp : '',
27617     
27618     
27619     defaultAutoCreate : { // modified by initCompnoent..
27620         tag: "input",
27621         type:"hidden"
27622     },
27623
27624     // private
27625     onRender : function(ct, position){
27626         
27627         Roo.form.Signature.superclass.onRender.call(this, ct, position);
27628         
27629         this.wrap = this.el.wrap({
27630             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27631         });
27632         
27633         this.createToolbar(this);
27634         this.signPanel = this.wrap.createChild({
27635                 tag: 'div',
27636                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27637             }, this.el
27638         );
27639             
27640         this.svgID = Roo.id();
27641         this.svgEl = this.signPanel.createChild({
27642               xmlns : 'http://www.w3.org/2000/svg',
27643               tag : 'svg',
27644               id : this.svgID + "-svg",
27645               width: this.width,
27646               height: this.height,
27647               viewBox: '0 0 '+this.width+' '+this.height,
27648               cn : [
27649                 {
27650                     tag: "rect",
27651                     id: this.svgID + "-svg-r",
27652                     width: this.width,
27653                     height: this.height,
27654                     fill: "#ffa"
27655                 },
27656                 {
27657                     tag: "line",
27658                     id: this.svgID + "-svg-l",
27659                     x1: "0", // start
27660                     y1: (this.height*0.8), // start set the line in 80% of height
27661                     x2: this.width, // end
27662                     y2: (this.height*0.8), // end set the line in 80% of height
27663                     'stroke': "#666",
27664                     'stroke-width': "1",
27665                     'stroke-dasharray': "3",
27666                     'shape-rendering': "crispEdges",
27667                     'pointer-events': "none"
27668                 },
27669                 {
27670                     tag: "path",
27671                     id: this.svgID + "-svg-p",
27672                     'stroke': "navy",
27673                     'stroke-width': "3",
27674                     'fill': "none",
27675                     'pointer-events': 'none'
27676                 }
27677               ]
27678         });
27679         this.createSVG();
27680         this.svgBox = this.svgEl.dom.getScreenCTM();
27681     },
27682     createSVG : function(){ 
27683         var svg = this.signPanel;
27684         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27685         var t = this;
27686
27687         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27688         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27689         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27690         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27691         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27692         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27693         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27694         
27695     },
27696     isTouchEvent : function(e){
27697         return e.type.match(/^touch/);
27698     },
27699     getCoords : function (e) {
27700         var pt    = this.svgEl.dom.createSVGPoint();
27701         pt.x = e.clientX; 
27702         pt.y = e.clientY;
27703         if (this.isTouchEvent(e)) {
27704             pt.x =  e.targetTouches[0].clientX;
27705             pt.y = e.targetTouches[0].clientY;
27706         }
27707         var a = this.svgEl.dom.getScreenCTM();
27708         var b = a.inverse();
27709         var mx = pt.matrixTransform(b);
27710         return mx.x + ',' + mx.y;
27711     },
27712     //mouse event headler 
27713     down : function (e) {
27714         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27715         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27716         
27717         this.isMouseDown = true;
27718         
27719         e.preventDefault();
27720     },
27721     move : function (e) {
27722         if (this.isMouseDown) {
27723             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27724             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27725         }
27726         
27727         e.preventDefault();
27728     },
27729     up : function (e) {
27730         this.isMouseDown = false;
27731         var sp = this.signatureTmp.split(' ');
27732         
27733         if(sp.length > 1){
27734             if(!sp[sp.length-2].match(/^L/)){
27735                 sp.pop();
27736                 sp.pop();
27737                 sp.push("");
27738                 this.signatureTmp = sp.join(" ");
27739             }
27740         }
27741         if(this.getValue() != this.signatureTmp){
27742             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27743             this.isConfirmed = false;
27744         }
27745         e.preventDefault();
27746     },
27747     
27748     /**
27749      * Protected method that will not generally be called directly. It
27750      * is called when the editor creates its toolbar. Override this method if you need to
27751      * add custom toolbar buttons.
27752      * @param {HtmlEditor} editor
27753      */
27754     createToolbar : function(editor){
27755          function btn(id, toggle, handler){
27756             var xid = fid + '-'+ id ;
27757             return {
27758                 id : xid,
27759                 cmd : id,
27760                 cls : 'x-btn-icon x-edit-'+id,
27761                 enableToggle:toggle !== false,
27762                 scope: editor, // was editor...
27763                 handler:handler||editor.relayBtnCmd,
27764                 clickEvent:'mousedown',
27765                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27766                 tabIndex:-1
27767             };
27768         }
27769         
27770         
27771         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27772         this.tb = tb;
27773         this.tb.add(
27774            {
27775                 cls : ' x-signature-btn x-signature-'+id,
27776                 scope: editor, // was editor...
27777                 handler: this.reset,
27778                 clickEvent:'mousedown',
27779                 text: this.labels.clear
27780             },
27781             {
27782                  xtype : 'Fill',
27783                  xns: Roo.Toolbar
27784             }, 
27785             {
27786                 cls : '  x-signature-btn x-signature-'+id,
27787                 scope: editor, // was editor...
27788                 handler: this.confirmHandler,
27789                 clickEvent:'mousedown',
27790                 text: this.labels.confirm
27791             }
27792         );
27793     
27794     },
27795     //public
27796     /**
27797      * when user is clicked confirm then show this image.....
27798      * 
27799      * @return {String} Image Data URI
27800      */
27801     getImageDataURI : function(){
27802         var svg = this.svgEl.dom.parentNode.innerHTML;
27803         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
27804         return src; 
27805     },
27806     /**
27807      * 
27808      * @return {Boolean} this.isConfirmed
27809      */
27810     getConfirmed : function(){
27811         return this.isConfirmed;
27812     },
27813     /**
27814      * 
27815      * @return {Number} this.width
27816      */
27817     getWidth : function(){
27818         return this.width;
27819     },
27820     /**
27821      * 
27822      * @return {Number} this.height
27823      */
27824     getHeight : function(){
27825         return this.height;
27826     },
27827     // private
27828     getSignature : function(){
27829         return this.signatureTmp;
27830     },
27831     // private
27832     reset : function(){
27833         this.signatureTmp = '';
27834         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27835         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
27836         this.isConfirmed = false;
27837         Roo.form.Signature.superclass.reset.call(this);
27838     },
27839     setSignature : function(s){
27840         this.signatureTmp = s;
27841         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27842         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
27843         this.setValue(s);
27844         this.isConfirmed = false;
27845         Roo.form.Signature.superclass.reset.call(this);
27846     }, 
27847     test : function(){
27848 //        Roo.log(this.signPanel.dom.contentWindow.up())
27849     },
27850     //private
27851     setConfirmed : function(){
27852         
27853         
27854         
27855 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
27856     },
27857     // private
27858     confirmHandler : function(){
27859         if(!this.getSignature()){
27860             return;
27861         }
27862         
27863         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
27864         this.setValue(this.getSignature());
27865         this.isConfirmed = true;
27866         
27867         this.fireEvent('confirm', this);
27868     },
27869     // private
27870     // Subclasses should provide the validation implementation by overriding this
27871     validateValue : function(value){
27872         if(this.allowBlank){
27873             return true;
27874         }
27875         
27876         if(this.isConfirmed){
27877             return true;
27878         }
27879         return false;
27880     }
27881 });/*
27882  * Based on:
27883  * Ext JS Library 1.1.1
27884  * Copyright(c) 2006-2007, Ext JS, LLC.
27885  *
27886  * Originally Released Under LGPL - original licence link has changed is not relivant.
27887  *
27888  * Fork - LGPL
27889  * <script type="text/javascript">
27890  */
27891  
27892
27893 /**
27894  * @class Roo.form.ComboBox
27895  * @extends Roo.form.TriggerField
27896  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
27897  * @constructor
27898  * Create a new ComboBox.
27899  * @param {Object} config Configuration options
27900  */
27901 Roo.form.Select = function(config){
27902     Roo.form.Select.superclass.constructor.call(this, config);
27903      
27904 };
27905
27906 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
27907     /**
27908      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
27909      */
27910     /**
27911      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
27912      * rendering into an Roo.Editor, defaults to false)
27913      */
27914     /**
27915      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
27916      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
27917      */
27918     /**
27919      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
27920      */
27921     /**
27922      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
27923      * the dropdown list (defaults to undefined, with no header element)
27924      */
27925
27926      /**
27927      * @cfg {String/Roo.Template} tpl The template to use to render the output
27928      */
27929      
27930     // private
27931     defaultAutoCreate : {tag: "select"  },
27932     /**
27933      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
27934      */
27935     listWidth: undefined,
27936     /**
27937      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
27938      * mode = 'remote' or 'text' if mode = 'local')
27939      */
27940     displayField: undefined,
27941     /**
27942      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
27943      * mode = 'remote' or 'value' if mode = 'local'). 
27944      * Note: use of a valueField requires the user make a selection
27945      * in order for a value to be mapped.
27946      */
27947     valueField: undefined,
27948     
27949     
27950     /**
27951      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
27952      * field's data value (defaults to the underlying DOM element's name)
27953      */
27954     hiddenName: undefined,
27955     /**
27956      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
27957      */
27958     listClass: '',
27959     /**
27960      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
27961      */
27962     selectedClass: 'x-combo-selected',
27963     /**
27964      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
27965      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
27966      * which displays a downward arrow icon).
27967      */
27968     triggerClass : 'x-form-arrow-trigger',
27969     /**
27970      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
27971      */
27972     shadow:'sides',
27973     /**
27974      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
27975      * anchor positions (defaults to 'tl-bl')
27976      */
27977     listAlign: 'tl-bl?',
27978     /**
27979      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
27980      */
27981     maxHeight: 300,
27982     /**
27983      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
27984      * query specified by the allQuery config option (defaults to 'query')
27985      */
27986     triggerAction: 'query',
27987     /**
27988      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
27989      * (defaults to 4, does not apply if editable = false)
27990      */
27991     minChars : 4,
27992     /**
27993      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
27994      * delay (typeAheadDelay) if it matches a known value (defaults to false)
27995      */
27996     typeAhead: false,
27997     /**
27998      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
27999      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
28000      */
28001     queryDelay: 500,
28002     /**
28003      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
28004      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
28005      */
28006     pageSize: 0,
28007     /**
28008      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
28009      * when editable = true (defaults to false)
28010      */
28011     selectOnFocus:false,
28012     /**
28013      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
28014      */
28015     queryParam: 'query',
28016     /**
28017      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
28018      * when mode = 'remote' (defaults to 'Loading...')
28019      */
28020     loadingText: 'Loading...',
28021     /**
28022      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
28023      */
28024     resizable: false,
28025     /**
28026      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
28027      */
28028     handleHeight : 8,
28029     /**
28030      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
28031      * traditional select (defaults to true)
28032      */
28033     editable: true,
28034     /**
28035      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
28036      */
28037     allQuery: '',
28038     /**
28039      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
28040      */
28041     mode: 'remote',
28042     /**
28043      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
28044      * listWidth has a higher value)
28045      */
28046     minListWidth : 70,
28047     /**
28048      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
28049      * allow the user to set arbitrary text into the field (defaults to false)
28050      */
28051     forceSelection:false,
28052     /**
28053      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
28054      * if typeAhead = true (defaults to 250)
28055      */
28056     typeAheadDelay : 250,
28057     /**
28058      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
28059      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
28060      */
28061     valueNotFoundText : undefined,
28062     
28063     /**
28064      * @cfg {String} defaultValue The value displayed after loading the store.
28065      */
28066     defaultValue: '',
28067     
28068     /**
28069      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
28070      */
28071     blockFocus : false,
28072     
28073     /**
28074      * @cfg {Boolean} disableClear Disable showing of clear button.
28075      */
28076     disableClear : false,
28077     /**
28078      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
28079      */
28080     alwaysQuery : false,
28081     
28082     //private
28083     addicon : false,
28084     editicon: false,
28085     
28086     // element that contains real text value.. (when hidden is used..)
28087      
28088     // private
28089     onRender : function(ct, position){
28090         Roo.form.Field.prototype.onRender.call(this, ct, position);
28091         
28092         if(this.store){
28093             this.store.on('beforeload', this.onBeforeLoad, this);
28094             this.store.on('load', this.onLoad, this);
28095             this.store.on('loadexception', this.onLoadException, this);
28096             this.store.load({});
28097         }
28098         
28099         
28100         
28101     },
28102
28103     // private
28104     initEvents : function(){
28105         //Roo.form.ComboBox.superclass.initEvents.call(this);
28106  
28107     },
28108
28109     onDestroy : function(){
28110        
28111         if(this.store){
28112             this.store.un('beforeload', this.onBeforeLoad, this);
28113             this.store.un('load', this.onLoad, this);
28114             this.store.un('loadexception', this.onLoadException, this);
28115         }
28116         //Roo.form.ComboBox.superclass.onDestroy.call(this);
28117     },
28118
28119     // private
28120     fireKey : function(e){
28121         if(e.isNavKeyPress() && !this.list.isVisible()){
28122             this.fireEvent("specialkey", this, e);
28123         }
28124     },
28125
28126     // private
28127     onResize: function(w, h){
28128         
28129         return; 
28130     
28131         
28132     },
28133
28134     /**
28135      * Allow or prevent the user from directly editing the field text.  If false is passed,
28136      * the user will only be able to select from the items defined in the dropdown list.  This method
28137      * is the runtime equivalent of setting the 'editable' config option at config time.
28138      * @param {Boolean} value True to allow the user to directly edit the field text
28139      */
28140     setEditable : function(value){
28141          
28142     },
28143
28144     // private
28145     onBeforeLoad : function(){
28146         
28147         Roo.log("Select before load");
28148         return;
28149     
28150         this.innerList.update(this.loadingText ?
28151                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
28152         //this.restrictHeight();
28153         this.selectedIndex = -1;
28154     },
28155
28156     // private
28157     onLoad : function(){
28158
28159     
28160         var dom = this.el.dom;
28161         dom.innerHTML = '';
28162          var od = dom.ownerDocument;
28163          
28164         if (this.emptyText) {
28165             var op = od.createElement('option');
28166             op.setAttribute('value', '');
28167             op.innerHTML = String.format('{0}', this.emptyText);
28168             dom.appendChild(op);
28169         }
28170         if(this.store.getCount() > 0){
28171            
28172             var vf = this.valueField;
28173             var df = this.displayField;
28174             this.store.data.each(function(r) {
28175                 // which colmsn to use... testing - cdoe / title..
28176                 var op = od.createElement('option');
28177                 op.setAttribute('value', r.data[vf]);
28178                 op.innerHTML = String.format('{0}', r.data[df]);
28179                 dom.appendChild(op);
28180             });
28181             if (typeof(this.defaultValue != 'undefined')) {
28182                 this.setValue(this.defaultValue);
28183             }
28184             
28185              
28186         }else{
28187             //this.onEmptyResults();
28188         }
28189         //this.el.focus();
28190     },
28191     // private
28192     onLoadException : function()
28193     {
28194         dom.innerHTML = '';
28195             
28196         Roo.log("Select on load exception");
28197         return;
28198     
28199         this.collapse();
28200         Roo.log(this.store.reader.jsonData);
28201         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
28202             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
28203         }
28204         
28205         
28206     },
28207     // private
28208     onTypeAhead : function(){
28209          
28210     },
28211
28212     // private
28213     onSelect : function(record, index){
28214         Roo.log('on select?');
28215         return;
28216         if(this.fireEvent('beforeselect', this, record, index) !== false){
28217             this.setFromData(index > -1 ? record.data : false);
28218             this.collapse();
28219             this.fireEvent('select', this, record, index);
28220         }
28221     },
28222
28223     /**
28224      * Returns the currently selected field value or empty string if no value is set.
28225      * @return {String} value The selected value
28226      */
28227     getValue : function(){
28228         var dom = this.el.dom;
28229         this.value = dom.options[dom.selectedIndex].value;
28230         return this.value;
28231         
28232     },
28233
28234     /**
28235      * Clears any text/value currently set in the field
28236      */
28237     clearValue : function(){
28238         this.value = '';
28239         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
28240         
28241     },
28242
28243     /**
28244      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
28245      * will be displayed in the field.  If the value does not match the data value of an existing item,
28246      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
28247      * Otherwise the field will be blank (although the value will still be set).
28248      * @param {String} value The value to match
28249      */
28250     setValue : function(v){
28251         var d = this.el.dom;
28252         for (var i =0; i < d.options.length;i++) {
28253             if (v == d.options[i].value) {
28254                 d.selectedIndex = i;
28255                 this.value = v;
28256                 return;
28257             }
28258         }
28259         this.clearValue();
28260     },
28261     /**
28262      * @property {Object} the last set data for the element
28263      */
28264     
28265     lastData : false,
28266     /**
28267      * Sets the value of the field based on a object which is related to the record format for the store.
28268      * @param {Object} value the value to set as. or false on reset?
28269      */
28270     setFromData : function(o){
28271         Roo.log('setfrom data?');
28272          
28273         
28274         
28275     },
28276     // private
28277     reset : function(){
28278         this.clearValue();
28279     },
28280     // private
28281     findRecord : function(prop, value){
28282         
28283         return false;
28284     
28285         var record;
28286         if(this.store.getCount() > 0){
28287             this.store.each(function(r){
28288                 if(r.data[prop] == value){
28289                     record = r;
28290                     return false;
28291                 }
28292                 return true;
28293             });
28294         }
28295         return record;
28296     },
28297     
28298     getName: function()
28299     {
28300         // returns hidden if it's set..
28301         if (!this.rendered) {return ''};
28302         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
28303         
28304     },
28305      
28306
28307     
28308
28309     // private
28310     onEmptyResults : function(){
28311         Roo.log('empty results');
28312         //this.collapse();
28313     },
28314
28315     /**
28316      * Returns true if the dropdown list is expanded, else false.
28317      */
28318     isExpanded : function(){
28319         return false;
28320     },
28321
28322     /**
28323      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
28324      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28325      * @param {String} value The data value of the item to select
28326      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28327      * selected item if it is not currently in view (defaults to true)
28328      * @return {Boolean} True if the value matched an item in the list, else false
28329      */
28330     selectByValue : function(v, scrollIntoView){
28331         Roo.log('select By Value');
28332         return false;
28333     
28334         if(v !== undefined && v !== null){
28335             var r = this.findRecord(this.valueField || this.displayField, v);
28336             if(r){
28337                 this.select(this.store.indexOf(r), scrollIntoView);
28338                 return true;
28339             }
28340         }
28341         return false;
28342     },
28343
28344     /**
28345      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
28346      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28347      * @param {Number} index The zero-based index of the list item to select
28348      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28349      * selected item if it is not currently in view (defaults to true)
28350      */
28351     select : function(index, scrollIntoView){
28352         Roo.log('select ');
28353         return  ;
28354         
28355         this.selectedIndex = index;
28356         this.view.select(index);
28357         if(scrollIntoView !== false){
28358             var el = this.view.getNode(index);
28359             if(el){
28360                 this.innerList.scrollChildIntoView(el, false);
28361             }
28362         }
28363     },
28364
28365       
28366
28367     // private
28368     validateBlur : function(){
28369         
28370         return;
28371         
28372     },
28373
28374     // private
28375     initQuery : function(){
28376         this.doQuery(this.getRawValue());
28377     },
28378
28379     // private
28380     doForce : function(){
28381         if(this.el.dom.value.length > 0){
28382             this.el.dom.value =
28383                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
28384              
28385         }
28386     },
28387
28388     /**
28389      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
28390      * query allowing the query action to be canceled if needed.
28391      * @param {String} query The SQL query to execute
28392      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
28393      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
28394      * saved in the current store (defaults to false)
28395      */
28396     doQuery : function(q, forceAll){
28397         
28398         Roo.log('doQuery?');
28399         if(q === undefined || q === null){
28400             q = '';
28401         }
28402         var qe = {
28403             query: q,
28404             forceAll: forceAll,
28405             combo: this,
28406             cancel:false
28407         };
28408         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
28409             return false;
28410         }
28411         q = qe.query;
28412         forceAll = qe.forceAll;
28413         if(forceAll === true || (q.length >= this.minChars)){
28414             if(this.lastQuery != q || this.alwaysQuery){
28415                 this.lastQuery = q;
28416                 if(this.mode == 'local'){
28417                     this.selectedIndex = -1;
28418                     if(forceAll){
28419                         this.store.clearFilter();
28420                     }else{
28421                         this.store.filter(this.displayField, q);
28422                     }
28423                     this.onLoad();
28424                 }else{
28425                     this.store.baseParams[this.queryParam] = q;
28426                     this.store.load({
28427                         params: this.getParams(q)
28428                     });
28429                     this.expand();
28430                 }
28431             }else{
28432                 this.selectedIndex = -1;
28433                 this.onLoad();   
28434             }
28435         }
28436     },
28437
28438     // private
28439     getParams : function(q){
28440         var p = {};
28441         //p[this.queryParam] = q;
28442         if(this.pageSize){
28443             p.start = 0;
28444             p.limit = this.pageSize;
28445         }
28446         return p;
28447     },
28448
28449     /**
28450      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
28451      */
28452     collapse : function(){
28453         
28454     },
28455
28456     // private
28457     collapseIf : function(e){
28458         
28459     },
28460
28461     /**
28462      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28463      */
28464     expand : function(){
28465         
28466     } ,
28467
28468     // private
28469      
28470
28471     /** 
28472     * @cfg {Boolean} grow 
28473     * @hide 
28474     */
28475     /** 
28476     * @cfg {Number} growMin 
28477     * @hide 
28478     */
28479     /** 
28480     * @cfg {Number} growMax 
28481     * @hide 
28482     */
28483     /**
28484      * @hide
28485      * @method autoSize
28486      */
28487     
28488     setWidth : function()
28489     {
28490         
28491     },
28492     getResizeEl : function(){
28493         return this.el;
28494     }
28495 });//<script type="text/javasscript">
28496  
28497
28498 /**
28499  * @class Roo.DDView
28500  * A DnD enabled version of Roo.View.
28501  * @param {Element/String} container The Element in which to create the View.
28502  * @param {String} tpl The template string used to create the markup for each element of the View
28503  * @param {Object} config The configuration properties. These include all the config options of
28504  * {@link Roo.View} plus some specific to this class.<br>
28505  * <p>
28506  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28507  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28508  * <p>
28509  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28510 .x-view-drag-insert-above {
28511         border-top:1px dotted #3366cc;
28512 }
28513 .x-view-drag-insert-below {
28514         border-bottom:1px dotted #3366cc;
28515 }
28516 </code></pre>
28517  * 
28518  */
28519  
28520 Roo.DDView = function(container, tpl, config) {
28521     Roo.DDView.superclass.constructor.apply(this, arguments);
28522     this.getEl().setStyle("outline", "0px none");
28523     this.getEl().unselectable();
28524     if (this.dragGroup) {
28525         this.setDraggable(this.dragGroup.split(","));
28526     }
28527     if (this.dropGroup) {
28528         this.setDroppable(this.dropGroup.split(","));
28529     }
28530     if (this.deletable) {
28531         this.setDeletable();
28532     }
28533     this.isDirtyFlag = false;
28534         this.addEvents({
28535                 "drop" : true
28536         });
28537 };
28538
28539 Roo.extend(Roo.DDView, Roo.View, {
28540 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28541 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28542 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28543 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28544
28545         isFormField: true,
28546
28547         reset: Roo.emptyFn,
28548         
28549         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28550
28551         validate: function() {
28552                 return true;
28553         },
28554         
28555         destroy: function() {
28556                 this.purgeListeners();
28557                 this.getEl.removeAllListeners();
28558                 this.getEl().remove();
28559                 if (this.dragZone) {
28560                         if (this.dragZone.destroy) {
28561                                 this.dragZone.destroy();
28562                         }
28563                 }
28564                 if (this.dropZone) {
28565                         if (this.dropZone.destroy) {
28566                                 this.dropZone.destroy();
28567                         }
28568                 }
28569         },
28570
28571 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28572         getName: function() {
28573                 return this.name;
28574         },
28575
28576 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28577         setValue: function(v) {
28578                 if (!this.store) {
28579                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28580                 }
28581                 var data = {};
28582                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28583                 this.store.proxy = new Roo.data.MemoryProxy(data);
28584                 this.store.load();
28585         },
28586
28587 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28588         getValue: function() {
28589                 var result = '(';
28590                 this.store.each(function(rec) {
28591                         result += rec.id + ',';
28592                 });
28593                 return result.substr(0, result.length - 1) + ')';
28594         },
28595         
28596         getIds: function() {
28597                 var i = 0, result = new Array(this.store.getCount());
28598                 this.store.each(function(rec) {
28599                         result[i++] = rec.id;
28600                 });
28601                 return result;
28602         },
28603         
28604         isDirty: function() {
28605                 return this.isDirtyFlag;
28606         },
28607
28608 /**
28609  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28610  *      whole Element becomes the target, and this causes the drop gesture to append.
28611  */
28612     getTargetFromEvent : function(e) {
28613                 var target = e.getTarget();
28614                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28615                 target = target.parentNode;
28616                 }
28617                 if (!target) {
28618                         target = this.el.dom.lastChild || this.el.dom;
28619                 }
28620                 return target;
28621     },
28622
28623 /**
28624  *      Create the drag data which consists of an object which has the property "ddel" as
28625  *      the drag proxy element. 
28626  */
28627     getDragData : function(e) {
28628         var target = this.findItemFromChild(e.getTarget());
28629                 if(target) {
28630                         this.handleSelection(e);
28631                         var selNodes = this.getSelectedNodes();
28632             var dragData = {
28633                 source: this,
28634                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28635                 nodes: selNodes,
28636                 records: []
28637                         };
28638                         var selectedIndices = this.getSelectedIndexes();
28639                         for (var i = 0; i < selectedIndices.length; i++) {
28640                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28641                         }
28642                         if (selNodes.length == 1) {
28643                                 dragData.ddel = target.cloneNode(true); // the div element
28644                         } else {
28645                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28646                                 div.className = 'multi-proxy';
28647                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28648                                         div.appendChild(selNodes[i].cloneNode(true));
28649                                 }
28650                                 dragData.ddel = div;
28651                         }
28652             //console.log(dragData)
28653             //console.log(dragData.ddel.innerHTML)
28654                         return dragData;
28655                 }
28656         //console.log('nodragData')
28657                 return false;
28658     },
28659     
28660 /**     Specify to which ddGroup items in this DDView may be dragged. */
28661     setDraggable: function(ddGroup) {
28662         if (ddGroup instanceof Array) {
28663                 Roo.each(ddGroup, this.setDraggable, this);
28664                 return;
28665         }
28666         if (this.dragZone) {
28667                 this.dragZone.addToGroup(ddGroup);
28668         } else {
28669                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28670                                 containerScroll: true,
28671                                 ddGroup: ddGroup 
28672
28673                         });
28674 //                      Draggability implies selection. DragZone's mousedown selects the element.
28675                         if (!this.multiSelect) { this.singleSelect = true; }
28676
28677 //                      Wire the DragZone's handlers up to methods in *this*
28678                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28679                 }
28680     },
28681
28682 /**     Specify from which ddGroup this DDView accepts drops. */
28683     setDroppable: function(ddGroup) {
28684         if (ddGroup instanceof Array) {
28685                 Roo.each(ddGroup, this.setDroppable, this);
28686                 return;
28687         }
28688         if (this.dropZone) {
28689                 this.dropZone.addToGroup(ddGroup);
28690         } else {
28691                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28692                                 containerScroll: true,
28693                                 ddGroup: ddGroup
28694                         });
28695
28696 //                      Wire the DropZone's handlers up to methods in *this*
28697                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28698                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28699                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28700                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28701                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28702                 }
28703     },
28704
28705 /**     Decide whether to drop above or below a View node. */
28706     getDropPoint : function(e, n, dd){
28707         if (n == this.el.dom) { return "above"; }
28708                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28709                 var c = t + (b - t) / 2;
28710                 var y = Roo.lib.Event.getPageY(e);
28711                 if(y <= c) {
28712                         return "above";
28713                 }else{
28714                         return "below";
28715                 }
28716     },
28717
28718     onNodeEnter : function(n, dd, e, data){
28719                 return false;
28720     },
28721     
28722     onNodeOver : function(n, dd, e, data){
28723                 var pt = this.getDropPoint(e, n, dd);
28724                 // set the insert point style on the target node
28725                 var dragElClass = this.dropNotAllowed;
28726                 if (pt) {
28727                         var targetElClass;
28728                         if (pt == "above"){
28729                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28730                                 targetElClass = "x-view-drag-insert-above";
28731                         } else {
28732                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28733                                 targetElClass = "x-view-drag-insert-below";
28734                         }
28735                         if (this.lastInsertClass != targetElClass){
28736                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28737                                 this.lastInsertClass = targetElClass;
28738                         }
28739                 }
28740                 return dragElClass;
28741         },
28742
28743     onNodeOut : function(n, dd, e, data){
28744                 this.removeDropIndicators(n);
28745     },
28746
28747     onNodeDrop : function(n, dd, e, data){
28748         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28749                 return false;
28750         }
28751         var pt = this.getDropPoint(e, n, dd);
28752                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28753                 if (pt == "below") { insertAt++; }
28754                 for (var i = 0; i < data.records.length; i++) {
28755                         var r = data.records[i];
28756                         var dup = this.store.getById(r.id);
28757                         if (dup && (dd != this.dragZone)) {
28758                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28759                         } else {
28760                                 if (data.copy) {
28761                                         this.store.insert(insertAt++, r.copy());
28762                                 } else {
28763                                         data.source.isDirtyFlag = true;
28764                                         r.store.remove(r);
28765                                         this.store.insert(insertAt++, r);
28766                                 }
28767                                 this.isDirtyFlag = true;
28768                         }
28769                 }
28770                 this.dragZone.cachedTarget = null;
28771                 return true;
28772     },
28773
28774     removeDropIndicators : function(n){
28775                 if(n){
28776                         Roo.fly(n).removeClass([
28777                                 "x-view-drag-insert-above",
28778                                 "x-view-drag-insert-below"]);
28779                         this.lastInsertClass = "_noclass";
28780                 }
28781     },
28782
28783 /**
28784  *      Utility method. Add a delete option to the DDView's context menu.
28785  *      @param {String} imageUrl The URL of the "delete" icon image.
28786  */
28787         setDeletable: function(imageUrl) {
28788                 if (!this.singleSelect && !this.multiSelect) {
28789                         this.singleSelect = true;
28790                 }
28791                 var c = this.getContextMenu();
28792                 this.contextMenu.on("itemclick", function(item) {
28793                         switch (item.id) {
28794                                 case "delete":
28795                                         this.remove(this.getSelectedIndexes());
28796                                         break;
28797                         }
28798                 }, this);
28799                 this.contextMenu.add({
28800                         icon: imageUrl,
28801                         id: "delete",
28802                         text: 'Delete'
28803                 });
28804         },
28805         
28806 /**     Return the context menu for this DDView. */
28807         getContextMenu: function() {
28808                 if (!this.contextMenu) {
28809 //                      Create the View's context menu
28810                         this.contextMenu = new Roo.menu.Menu({
28811                                 id: this.id + "-contextmenu"
28812                         });
28813                         this.el.on("contextmenu", this.showContextMenu, this);
28814                 }
28815                 return this.contextMenu;
28816         },
28817         
28818         disableContextMenu: function() {
28819                 if (this.contextMenu) {
28820                         this.el.un("contextmenu", this.showContextMenu, this);
28821                 }
28822         },
28823
28824         showContextMenu: function(e, item) {
28825         item = this.findItemFromChild(e.getTarget());
28826                 if (item) {
28827                         e.stopEvent();
28828                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28829                         this.contextMenu.showAt(e.getXY());
28830             }
28831     },
28832
28833 /**
28834  *      Remove {@link Roo.data.Record}s at the specified indices.
28835  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28836  */
28837     remove: function(selectedIndices) {
28838                 selectedIndices = [].concat(selectedIndices);
28839                 for (var i = 0; i < selectedIndices.length; i++) {
28840                         var rec = this.store.getAt(selectedIndices[i]);
28841                         this.store.remove(rec);
28842                 }
28843     },
28844
28845 /**
28846  *      Double click fires the event, but also, if this is draggable, and there is only one other
28847  *      related DropZone, it transfers the selected node.
28848  */
28849     onDblClick : function(e){
28850         var item = this.findItemFromChild(e.getTarget());
28851         if(item){
28852             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28853                 return false;
28854             }
28855             if (this.dragGroup) {
28856                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28857                     while (targets.indexOf(this.dropZone) > -1) {
28858                             targets.remove(this.dropZone);
28859                                 }
28860                     if (targets.length == 1) {
28861                                         this.dragZone.cachedTarget = null;
28862                         var el = Roo.get(targets[0].getEl());
28863                         var box = el.getBox(true);
28864                         targets[0].onNodeDrop(el.dom, {
28865                                 target: el.dom,
28866                                 xy: [box.x, box.y + box.height - 1]
28867                         }, null, this.getDragData(e));
28868                     }
28869                 }
28870         }
28871     },
28872     
28873     handleSelection: function(e) {
28874                 this.dragZone.cachedTarget = null;
28875         var item = this.findItemFromChild(e.getTarget());
28876         if (!item) {
28877                 this.clearSelections(true);
28878                 return;
28879         }
28880                 if (item && (this.multiSelect || this.singleSelect)){
28881                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28882                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28883                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28884                                 this.unselect(item);
28885                         } else {
28886                                 this.select(item, this.multiSelect && e.ctrlKey);
28887                                 this.lastSelection = item;
28888                         }
28889                 }
28890     },
28891
28892     onItemClick : function(item, index, e){
28893                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28894                         return false;
28895                 }
28896                 return true;
28897     },
28898
28899     unselect : function(nodeInfo, suppressEvent){
28900                 var node = this.getNode(nodeInfo);
28901                 if(node && this.isSelected(node)){
28902                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28903                                 Roo.fly(node).removeClass(this.selectedClass);
28904                                 this.selections.remove(node);
28905                                 if(!suppressEvent){
28906                                         this.fireEvent("selectionchange", this, this.selections);
28907                                 }
28908                         }
28909                 }
28910     }
28911 });
28912 /*
28913  * Based on:
28914  * Ext JS Library 1.1.1
28915  * Copyright(c) 2006-2007, Ext JS, LLC.
28916  *
28917  * Originally Released Under LGPL - original licence link has changed is not relivant.
28918  *
28919  * Fork - LGPL
28920  * <script type="text/javascript">
28921  */
28922  
28923 /**
28924  * @class Roo.LayoutManager
28925  * @extends Roo.util.Observable
28926  * Base class for layout managers.
28927  */
28928 Roo.LayoutManager = function(container, config){
28929     Roo.LayoutManager.superclass.constructor.call(this);
28930     this.el = Roo.get(container);
28931     // ie scrollbar fix
28932     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28933         document.body.scroll = "no";
28934     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28935         this.el.position('relative');
28936     }
28937     this.id = this.el.id;
28938     this.el.addClass("x-layout-container");
28939     /** false to disable window resize monitoring @type Boolean */
28940     this.monitorWindowResize = true;
28941     this.regions = {};
28942     this.addEvents({
28943         /**
28944          * @event layout
28945          * Fires when a layout is performed. 
28946          * @param {Roo.LayoutManager} this
28947          */
28948         "layout" : true,
28949         /**
28950          * @event regionresized
28951          * Fires when the user resizes a region. 
28952          * @param {Roo.LayoutRegion} region The resized region
28953          * @param {Number} newSize The new size (width for east/west, height for north/south)
28954          */
28955         "regionresized" : true,
28956         /**
28957          * @event regioncollapsed
28958          * Fires when a region is collapsed. 
28959          * @param {Roo.LayoutRegion} region The collapsed region
28960          */
28961         "regioncollapsed" : true,
28962         /**
28963          * @event regionexpanded
28964          * Fires when a region is expanded.  
28965          * @param {Roo.LayoutRegion} region The expanded region
28966          */
28967         "regionexpanded" : true
28968     });
28969     this.updating = false;
28970     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28971 };
28972
28973 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28974     /**
28975      * Returns true if this layout is currently being updated
28976      * @return {Boolean}
28977      */
28978     isUpdating : function(){
28979         return this.updating; 
28980     },
28981     
28982     /**
28983      * Suspend the LayoutManager from doing auto-layouts while
28984      * making multiple add or remove calls
28985      */
28986     beginUpdate : function(){
28987         this.updating = true;    
28988     },
28989     
28990     /**
28991      * Restore auto-layouts and optionally disable the manager from performing a layout
28992      * @param {Boolean} noLayout true to disable a layout update 
28993      */
28994     endUpdate : function(noLayout){
28995         this.updating = false;
28996         if(!noLayout){
28997             this.layout();
28998         }    
28999     },
29000     
29001     layout: function(){
29002         
29003     },
29004     
29005     onRegionResized : function(region, newSize){
29006         this.fireEvent("regionresized", region, newSize);
29007         this.layout();
29008     },
29009     
29010     onRegionCollapsed : function(region){
29011         this.fireEvent("regioncollapsed", region);
29012     },
29013     
29014     onRegionExpanded : function(region){
29015         this.fireEvent("regionexpanded", region);
29016     },
29017         
29018     /**
29019      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
29020      * performs box-model adjustments.
29021      * @return {Object} The size as an object {width: (the width), height: (the height)}
29022      */
29023     getViewSize : function(){
29024         var size;
29025         if(this.el.dom != document.body){
29026             size = this.el.getSize();
29027         }else{
29028             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
29029         }
29030         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
29031         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29032         return size;
29033     },
29034     
29035     /**
29036      * Returns the Element this layout is bound to.
29037      * @return {Roo.Element}
29038      */
29039     getEl : function(){
29040         return this.el;
29041     },
29042     
29043     /**
29044      * Returns the specified region.
29045      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
29046      * @return {Roo.LayoutRegion}
29047      */
29048     getRegion : function(target){
29049         return this.regions[target.toLowerCase()];
29050     },
29051     
29052     onWindowResize : function(){
29053         if(this.monitorWindowResize){
29054             this.layout();
29055         }
29056     }
29057 });/*
29058  * Based on:
29059  * Ext JS Library 1.1.1
29060  * Copyright(c) 2006-2007, Ext JS, LLC.
29061  *
29062  * Originally Released Under LGPL - original licence link has changed is not relivant.
29063  *
29064  * Fork - LGPL
29065  * <script type="text/javascript">
29066  */
29067 /**
29068  * @class Roo.BorderLayout
29069  * @extends Roo.LayoutManager
29070  * @children Roo.ContentPanel
29071  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
29072  * please see: <br><br>
29073  * <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>
29074  * <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>
29075  * Example:
29076  <pre><code>
29077  var layout = new Roo.BorderLayout(document.body, {
29078     north: {
29079         initialSize: 25,
29080         titlebar: false
29081     },
29082     west: {
29083         split:true,
29084         initialSize: 200,
29085         minSize: 175,
29086         maxSize: 400,
29087         titlebar: true,
29088         collapsible: true
29089     },
29090     east: {
29091         split:true,
29092         initialSize: 202,
29093         minSize: 175,
29094         maxSize: 400,
29095         titlebar: true,
29096         collapsible: true
29097     },
29098     south: {
29099         split:true,
29100         initialSize: 100,
29101         minSize: 100,
29102         maxSize: 200,
29103         titlebar: true,
29104         collapsible: true
29105     },
29106     center: {
29107         titlebar: true,
29108         autoScroll:true,
29109         resizeTabs: true,
29110         minTabWidth: 50,
29111         preferredTabWidth: 150
29112     }
29113 });
29114
29115 // shorthand
29116 var CP = Roo.ContentPanel;
29117
29118 layout.beginUpdate();
29119 layout.add("north", new CP("north", "North"));
29120 layout.add("south", new CP("south", {title: "South", closable: true}));
29121 layout.add("west", new CP("west", {title: "West"}));
29122 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29123 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29124 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29125 layout.getRegion("center").showPanel("center1");
29126 layout.endUpdate();
29127 </code></pre>
29128
29129 <b>The container the layout is rendered into can be either the body element or any other element.
29130 If it is not the body element, the container needs to either be an absolute positioned element,
29131 or you will need to add "position:relative" to the css of the container.  You will also need to specify
29132 the container size if it is not the body element.</b>
29133
29134 * @constructor
29135 * Create a new BorderLayout
29136 * @param {String/HTMLElement/Element} container The container this layout is bound to
29137 * @param {Object} config Configuration options
29138  */
29139 Roo.BorderLayout = function(container, config){
29140     config = config || {};
29141     Roo.BorderLayout.superclass.constructor.call(this, container, config);
29142     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29143     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29144         var target = this.factory.validRegions[i];
29145         if(config[target]){
29146             this.addRegion(target, config[target]);
29147         }
29148     }
29149 };
29150
29151 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29152         
29153         /**
29154          * @cfg {Roo.LayoutRegion} east
29155          */
29156         /**
29157          * @cfg {Roo.LayoutRegion} west
29158          */
29159         /**
29160          * @cfg {Roo.LayoutRegion} north
29161          */
29162         /**
29163          * @cfg {Roo.LayoutRegion} south
29164          */
29165         /**
29166          * @cfg {Roo.LayoutRegion} center
29167          */
29168     /**
29169      * Creates and adds a new region if it doesn't already exist.
29170      * @param {String} target The target region key (north, south, east, west or center).
29171      * @param {Object} config The regions config object
29172      * @return {BorderLayoutRegion} The new region
29173      */
29174     addRegion : function(target, config){
29175         if(!this.regions[target]){
29176             var r = this.factory.create(target, this, config);
29177             this.bindRegion(target, r);
29178         }
29179         return this.regions[target];
29180     },
29181
29182     // private (kinda)
29183     bindRegion : function(name, r){
29184         this.regions[name] = r;
29185         r.on("visibilitychange", this.layout, this);
29186         r.on("paneladded", this.layout, this);
29187         r.on("panelremoved", this.layout, this);
29188         r.on("invalidated", this.layout, this);
29189         r.on("resized", this.onRegionResized, this);
29190         r.on("collapsed", this.onRegionCollapsed, this);
29191         r.on("expanded", this.onRegionExpanded, this);
29192     },
29193
29194     /**
29195      * Performs a layout update.
29196      */
29197     layout : function(){
29198         if(this.updating) {
29199             return;
29200         }
29201         var size = this.getViewSize();
29202         var w = size.width;
29203         var h = size.height;
29204         var centerW = w;
29205         var centerH = h;
29206         var centerY = 0;
29207         var centerX = 0;
29208         //var x = 0, y = 0;
29209
29210         var rs = this.regions;
29211         var north = rs["north"];
29212         var south = rs["south"]; 
29213         var west = rs["west"];
29214         var east = rs["east"];
29215         var center = rs["center"];
29216         //if(this.hideOnLayout){ // not supported anymore
29217             //c.el.setStyle("display", "none");
29218         //}
29219         if(north && north.isVisible()){
29220             var b = north.getBox();
29221             var m = north.getMargins();
29222             b.width = w - (m.left+m.right);
29223             b.x = m.left;
29224             b.y = m.top;
29225             centerY = b.height + b.y + m.bottom;
29226             centerH -= centerY;
29227             north.updateBox(this.safeBox(b));
29228         }
29229         if(south && south.isVisible()){
29230             var b = south.getBox();
29231             var m = south.getMargins();
29232             b.width = w - (m.left+m.right);
29233             b.x = m.left;
29234             var totalHeight = (b.height + m.top + m.bottom);
29235             b.y = h - totalHeight + m.top;
29236             centerH -= totalHeight;
29237             south.updateBox(this.safeBox(b));
29238         }
29239         if(west && west.isVisible()){
29240             var b = west.getBox();
29241             var m = west.getMargins();
29242             b.height = centerH - (m.top+m.bottom);
29243             b.x = m.left;
29244             b.y = centerY + m.top;
29245             var totalWidth = (b.width + m.left + m.right);
29246             centerX += totalWidth;
29247             centerW -= totalWidth;
29248             west.updateBox(this.safeBox(b));
29249         }
29250         if(east && east.isVisible()){
29251             var b = east.getBox();
29252             var m = east.getMargins();
29253             b.height = centerH - (m.top+m.bottom);
29254             var totalWidth = (b.width + m.left + m.right);
29255             b.x = w - totalWidth + m.left;
29256             b.y = centerY + m.top;
29257             centerW -= totalWidth;
29258             east.updateBox(this.safeBox(b));
29259         }
29260         if(center){
29261             var m = center.getMargins();
29262             var centerBox = {
29263                 x: centerX + m.left,
29264                 y: centerY + m.top,
29265                 width: centerW - (m.left+m.right),
29266                 height: centerH - (m.top+m.bottom)
29267             };
29268             //if(this.hideOnLayout){
29269                 //center.el.setStyle("display", "block");
29270             //}
29271             center.updateBox(this.safeBox(centerBox));
29272         }
29273         this.el.repaint();
29274         this.fireEvent("layout", this);
29275     },
29276
29277     // private
29278     safeBox : function(box){
29279         box.width = Math.max(0, box.width);
29280         box.height = Math.max(0, box.height);
29281         return box;
29282     },
29283
29284     /**
29285      * Adds a ContentPanel (or subclass) to this layout.
29286      * @param {String} target The target region key (north, south, east, west or center).
29287      * @param {Roo.ContentPanel} panel The panel to add
29288      * @return {Roo.ContentPanel} The added panel
29289      */
29290     add : function(target, panel){
29291          
29292         target = target.toLowerCase();
29293         return this.regions[target].add(panel);
29294     },
29295
29296     /**
29297      * Remove a ContentPanel (or subclass) to this layout.
29298      * @param {String} target The target region key (north, south, east, west or center).
29299      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29300      * @return {Roo.ContentPanel} The removed panel
29301      */
29302     remove : function(target, panel){
29303         target = target.toLowerCase();
29304         return this.regions[target].remove(panel);
29305     },
29306
29307     /**
29308      * Searches all regions for a panel with the specified id
29309      * @param {String} panelId
29310      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29311      */
29312     findPanel : function(panelId){
29313         var rs = this.regions;
29314         for(var target in rs){
29315             if(typeof rs[target] != "function"){
29316                 var p = rs[target].getPanel(panelId);
29317                 if(p){
29318                     return p;
29319                 }
29320             }
29321         }
29322         return null;
29323     },
29324
29325     /**
29326      * Searches all regions for a panel with the specified id and activates (shows) it.
29327      * @param {String/ContentPanel} panelId The panels id or the panel itself
29328      * @return {Roo.ContentPanel} The shown panel or null
29329      */
29330     showPanel : function(panelId) {
29331       var rs = this.regions;
29332       for(var target in rs){
29333          var r = rs[target];
29334          if(typeof r != "function"){
29335             if(r.hasPanel(panelId)){
29336                return r.showPanel(panelId);
29337             }
29338          }
29339       }
29340       return null;
29341    },
29342
29343    /**
29344      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29345      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29346      */
29347     restoreState : function(provider){
29348         if(!provider){
29349             provider = Roo.state.Manager;
29350         }
29351         var sm = new Roo.LayoutStateManager();
29352         sm.init(this, provider);
29353     },
29354
29355     /**
29356      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29357      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29358      * a valid ContentPanel config object.  Example:
29359      * <pre><code>
29360 // Create the main layout
29361 var layout = new Roo.BorderLayout('main-ct', {
29362     west: {
29363         split:true,
29364         minSize: 175,
29365         titlebar: true
29366     },
29367     center: {
29368         title:'Components'
29369     }
29370 }, 'main-ct');
29371
29372 // Create and add multiple ContentPanels at once via configs
29373 layout.batchAdd({
29374    west: {
29375        id: 'source-files',
29376        autoCreate:true,
29377        title:'Ext Source Files',
29378        autoScroll:true,
29379        fitToFrame:true
29380    },
29381    center : {
29382        el: cview,
29383        autoScroll:true,
29384        fitToFrame:true,
29385        toolbar: tb,
29386        resizeEl:'cbody'
29387    }
29388 });
29389 </code></pre>
29390      * @param {Object} regions An object containing ContentPanel configs by region name
29391      */
29392     batchAdd : function(regions){
29393         this.beginUpdate();
29394         for(var rname in regions){
29395             var lr = this.regions[rname];
29396             if(lr){
29397                 this.addTypedPanels(lr, regions[rname]);
29398             }
29399         }
29400         this.endUpdate();
29401     },
29402
29403     // private
29404     addTypedPanels : function(lr, ps){
29405         if(typeof ps == 'string'){
29406             lr.add(new Roo.ContentPanel(ps));
29407         }
29408         else if(ps instanceof Array){
29409             for(var i =0, len = ps.length; i < len; i++){
29410                 this.addTypedPanels(lr, ps[i]);
29411             }
29412         }
29413         else if(!ps.events){ // raw config?
29414             var el = ps.el;
29415             delete ps.el; // prevent conflict
29416             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29417         }
29418         else {  // panel object assumed!
29419             lr.add(ps);
29420         }
29421     },
29422     /**
29423      * Adds a xtype elements to the layout.
29424      * <pre><code>
29425
29426 layout.addxtype({
29427        xtype : 'ContentPanel',
29428        region: 'west',
29429        items: [ .... ]
29430    }
29431 );
29432
29433 layout.addxtype({
29434         xtype : 'NestedLayoutPanel',
29435         region: 'west',
29436         layout: {
29437            center: { },
29438            west: { }   
29439         },
29440         items : [ ... list of content panels or nested layout panels.. ]
29441    }
29442 );
29443 </code></pre>
29444      * @param {Object} cfg Xtype definition of item to add.
29445      */
29446     addxtype : function(cfg)
29447     {
29448         // basically accepts a pannel...
29449         // can accept a layout region..!?!?
29450         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
29451         
29452         if (!cfg.xtype.match(/Panel$/)) {
29453             return false;
29454         }
29455         var ret = false;
29456         
29457         if (typeof(cfg.region) == 'undefined') {
29458             Roo.log("Failed to add Panel, region was not set");
29459             Roo.log(cfg);
29460             return false;
29461         }
29462         var region = cfg.region;
29463         delete cfg.region;
29464         
29465           
29466         var xitems = [];
29467         if (cfg.items) {
29468             xitems = cfg.items;
29469             delete cfg.items;
29470         }
29471         var nb = false;
29472         
29473         switch(cfg.xtype) 
29474         {
29475             case 'ContentPanel':  // ContentPanel (el, cfg)
29476             case 'ScrollPanel':  // ContentPanel (el, cfg)
29477             case 'ViewPanel': 
29478                 if(cfg.autoCreate) {
29479                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29480                 } else {
29481                     var el = this.el.createChild();
29482                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29483                 }
29484                 
29485                 this.add(region, ret);
29486                 break;
29487             
29488             
29489             case 'TreePanel': // our new panel!
29490                 cfg.el = this.el.createChild();
29491                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29492                 this.add(region, ret);
29493                 break;
29494             
29495             case 'NestedLayoutPanel': 
29496                 // create a new Layout (which is  a Border Layout...
29497                 var el = this.el.createChild();
29498                 var clayout = cfg.layout;
29499                 delete cfg.layout;
29500                 clayout.items   = clayout.items  || [];
29501                 // replace this exitems with the clayout ones..
29502                 xitems = clayout.items;
29503                  
29504                 
29505                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29506                     cfg.background = false;
29507                 }
29508                 var layout = new Roo.BorderLayout(el, clayout);
29509                 
29510                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29511                 //console.log('adding nested layout panel '  + cfg.toSource());
29512                 this.add(region, ret);
29513                 nb = {}; /// find first...
29514                 break;
29515                 
29516             case 'GridPanel': 
29517             
29518                 // needs grid and region
29519                 
29520                 //var el = this.getRegion(region).el.createChild();
29521                 var el = this.el.createChild();
29522                 // create the grid first...
29523                 
29524                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29525                 delete cfg.grid;
29526                 if (region == 'center' && this.active ) {
29527                     cfg.background = false;
29528                 }
29529                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29530                 
29531                 this.add(region, ret);
29532                 if (cfg.background) {
29533                     ret.on('activate', function(gp) {
29534                         if (!gp.grid.rendered) {
29535                             gp.grid.render();
29536                         }
29537                     });
29538                 } else {
29539                     grid.render();
29540                 }
29541                 break;
29542            
29543            
29544            
29545                 
29546                 
29547                 
29548             default:
29549                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29550                     
29551                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29552                     this.add(region, ret);
29553                 } else {
29554                 
29555                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29556                     return null;
29557                 }
29558                 
29559              // GridPanel (grid, cfg)
29560             
29561         }
29562         this.beginUpdate();
29563         // add children..
29564         var region = '';
29565         var abn = {};
29566         Roo.each(xitems, function(i)  {
29567             region = nb && i.region ? i.region : false;
29568             
29569             var add = ret.addxtype(i);
29570            
29571             if (region) {
29572                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29573                 if (!i.background) {
29574                     abn[region] = nb[region] ;
29575                 }
29576             }
29577             
29578         });
29579         this.endUpdate();
29580
29581         // make the last non-background panel active..
29582         //if (nb) { Roo.log(abn); }
29583         if (nb) {
29584             
29585             for(var r in abn) {
29586                 region = this.getRegion(r);
29587                 if (region) {
29588                     // tried using nb[r], but it does not work..
29589                      
29590                     region.showPanel(abn[r]);
29591                    
29592                 }
29593             }
29594         }
29595         return ret;
29596         
29597     }
29598 });
29599
29600 /**
29601  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29602  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29603  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29604  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29605  * <pre><code>
29606 // shorthand
29607 var CP = Roo.ContentPanel;
29608
29609 var layout = Roo.BorderLayout.create({
29610     north: {
29611         initialSize: 25,
29612         titlebar: false,
29613         panels: [new CP("north", "North")]
29614     },
29615     west: {
29616         split:true,
29617         initialSize: 200,
29618         minSize: 175,
29619         maxSize: 400,
29620         titlebar: true,
29621         collapsible: true,
29622         panels: [new CP("west", {title: "West"})]
29623     },
29624     east: {
29625         split:true,
29626         initialSize: 202,
29627         minSize: 175,
29628         maxSize: 400,
29629         titlebar: true,
29630         collapsible: true,
29631         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29632     },
29633     south: {
29634         split:true,
29635         initialSize: 100,
29636         minSize: 100,
29637         maxSize: 200,
29638         titlebar: true,
29639         collapsible: true,
29640         panels: [new CP("south", {title: "South", closable: true})]
29641     },
29642     center: {
29643         titlebar: true,
29644         autoScroll:true,
29645         resizeTabs: true,
29646         minTabWidth: 50,
29647         preferredTabWidth: 150,
29648         panels: [
29649             new CP("center1", {title: "Close Me", closable: true}),
29650             new CP("center2", {title: "Center Panel", closable: false})
29651         ]
29652     }
29653 }, document.body);
29654
29655 layout.getRegion("center").showPanel("center1");
29656 </code></pre>
29657  * @param config
29658  * @param targetEl
29659  */
29660 Roo.BorderLayout.create = function(config, targetEl){
29661     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29662     layout.beginUpdate();
29663     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29664     for(var j = 0, jlen = regions.length; j < jlen; j++){
29665         var lr = regions[j];
29666         if(layout.regions[lr] && config[lr].panels){
29667             var r = layout.regions[lr];
29668             var ps = config[lr].panels;
29669             layout.addTypedPanels(r, ps);
29670         }
29671     }
29672     layout.endUpdate();
29673     return layout;
29674 };
29675
29676 // private
29677 Roo.BorderLayout.RegionFactory = {
29678     // private
29679     validRegions : ["north","south","east","west","center"],
29680
29681     // private
29682     create : function(target, mgr, config){
29683         target = target.toLowerCase();
29684         if(config.lightweight || config.basic){
29685             return new Roo.BasicLayoutRegion(mgr, config, target);
29686         }
29687         switch(target){
29688             case "north":
29689                 return new Roo.NorthLayoutRegion(mgr, config);
29690             case "south":
29691                 return new Roo.SouthLayoutRegion(mgr, config);
29692             case "east":
29693                 return new Roo.EastLayoutRegion(mgr, config);
29694             case "west":
29695                 return new Roo.WestLayoutRegion(mgr, config);
29696             case "center":
29697                 return new Roo.CenterLayoutRegion(mgr, config);
29698         }
29699         throw 'Layout region "'+target+'" not supported.';
29700     }
29701 };/*
29702  * Based on:
29703  * Ext JS Library 1.1.1
29704  * Copyright(c) 2006-2007, Ext JS, LLC.
29705  *
29706  * Originally Released Under LGPL - original licence link has changed is not relivant.
29707  *
29708  * Fork - LGPL
29709  * <script type="text/javascript">
29710  */
29711  
29712 /**
29713  * @class Roo.BasicLayoutRegion
29714  * @extends Roo.util.Observable
29715  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29716  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29717  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29718  */
29719 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29720     this.mgr = mgr;
29721     this.position  = pos;
29722     this.events = {
29723         /**
29724          * @scope Roo.BasicLayoutRegion
29725          */
29726         
29727         /**
29728          * @event beforeremove
29729          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29730          * @param {Roo.LayoutRegion} this
29731          * @param {Roo.ContentPanel} panel The panel
29732          * @param {Object} e The cancel event object
29733          */
29734         "beforeremove" : true,
29735         /**
29736          * @event invalidated
29737          * Fires when the layout for this region is changed.
29738          * @param {Roo.LayoutRegion} this
29739          */
29740         "invalidated" : true,
29741         /**
29742          * @event visibilitychange
29743          * Fires when this region is shown or hidden 
29744          * @param {Roo.LayoutRegion} this
29745          * @param {Boolean} visibility true or false
29746          */
29747         "visibilitychange" : true,
29748         /**
29749          * @event paneladded
29750          * Fires when a panel is added. 
29751          * @param {Roo.LayoutRegion} this
29752          * @param {Roo.ContentPanel} panel The panel
29753          */
29754         "paneladded" : true,
29755         /**
29756          * @event panelremoved
29757          * Fires when a panel is removed. 
29758          * @param {Roo.LayoutRegion} this
29759          * @param {Roo.ContentPanel} panel The panel
29760          */
29761         "panelremoved" : true,
29762         /**
29763          * @event beforecollapse
29764          * Fires when this region before collapse.
29765          * @param {Roo.LayoutRegion} this
29766          */
29767         "beforecollapse" : true,
29768         /**
29769          * @event collapsed
29770          * Fires when this region is collapsed.
29771          * @param {Roo.LayoutRegion} this
29772          */
29773         "collapsed" : true,
29774         /**
29775          * @event expanded
29776          * Fires when this region is expanded.
29777          * @param {Roo.LayoutRegion} this
29778          */
29779         "expanded" : true,
29780         /**
29781          * @event slideshow
29782          * Fires when this region is slid into view.
29783          * @param {Roo.LayoutRegion} this
29784          */
29785         "slideshow" : true,
29786         /**
29787          * @event slidehide
29788          * Fires when this region slides out of view. 
29789          * @param {Roo.LayoutRegion} this
29790          */
29791         "slidehide" : true,
29792         /**
29793          * @event panelactivated
29794          * Fires when a panel is activated. 
29795          * @param {Roo.LayoutRegion} this
29796          * @param {Roo.ContentPanel} panel The activated panel
29797          */
29798         "panelactivated" : true,
29799         /**
29800          * @event resized
29801          * Fires when the user resizes this region. 
29802          * @param {Roo.LayoutRegion} this
29803          * @param {Number} newSize The new size (width for east/west, height for north/south)
29804          */
29805         "resized" : true
29806     };
29807     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29808     this.panels = new Roo.util.MixedCollection();
29809     this.panels.getKey = this.getPanelId.createDelegate(this);
29810     this.box = null;
29811     this.activePanel = null;
29812     // ensure listeners are added...
29813     
29814     if (config.listeners || config.events) {
29815         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29816             listeners : config.listeners || {},
29817             events : config.events || {}
29818         });
29819     }
29820     
29821     if(skipConfig !== true){
29822         this.applyConfig(config);
29823     }
29824 };
29825
29826 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29827     getPanelId : function(p){
29828         return p.getId();
29829     },
29830     
29831     applyConfig : function(config){
29832         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29833         this.config = config;
29834         
29835     },
29836     
29837     /**
29838      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29839      * the width, for horizontal (north, south) the height.
29840      * @param {Number} newSize The new width or height
29841      */
29842     resizeTo : function(newSize){
29843         var el = this.el ? this.el :
29844                  (this.activePanel ? this.activePanel.getEl() : null);
29845         if(el){
29846             switch(this.position){
29847                 case "east":
29848                 case "west":
29849                     el.setWidth(newSize);
29850                     this.fireEvent("resized", this, newSize);
29851                 break;
29852                 case "north":
29853                 case "south":
29854                     el.setHeight(newSize);
29855                     this.fireEvent("resized", this, newSize);
29856                 break;                
29857             }
29858         }
29859     },
29860     
29861     getBox : function(){
29862         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29863     },
29864     
29865     getMargins : function(){
29866         return this.margins;
29867     },
29868     
29869     updateBox : function(box){
29870         this.box = box;
29871         var el = this.activePanel.getEl();
29872         el.dom.style.left = box.x + "px";
29873         el.dom.style.top = box.y + "px";
29874         this.activePanel.setSize(box.width, box.height);
29875     },
29876     
29877     /**
29878      * Returns the container element for this region.
29879      * @return {Roo.Element}
29880      */
29881     getEl : function(){
29882         return this.activePanel;
29883     },
29884     
29885     /**
29886      * Returns true if this region is currently visible.
29887      * @return {Boolean}
29888      */
29889     isVisible : function(){
29890         return this.activePanel ? true : false;
29891     },
29892     
29893     setActivePanel : function(panel){
29894         panel = this.getPanel(panel);
29895         if(this.activePanel && this.activePanel != panel){
29896             this.activePanel.setActiveState(false);
29897             this.activePanel.getEl().setLeftTop(-10000,-10000);
29898         }
29899         this.activePanel = panel;
29900         panel.setActiveState(true);
29901         if(this.box){
29902             panel.setSize(this.box.width, this.box.height);
29903         }
29904         this.fireEvent("panelactivated", this, panel);
29905         this.fireEvent("invalidated");
29906     },
29907     
29908     /**
29909      * Show the specified panel.
29910      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29911      * @return {Roo.ContentPanel} The shown panel or null
29912      */
29913     showPanel : function(panel){
29914         if(panel = this.getPanel(panel)){
29915             this.setActivePanel(panel);
29916         }
29917         return panel;
29918     },
29919     
29920     /**
29921      * Get the active panel for this region.
29922      * @return {Roo.ContentPanel} The active panel or null
29923      */
29924     getActivePanel : function(){
29925         return this.activePanel;
29926     },
29927     
29928     /**
29929      * Add the passed ContentPanel(s)
29930      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29931      * @return {Roo.ContentPanel} The panel added (if only one was added)
29932      */
29933     add : function(panel){
29934         if(arguments.length > 1){
29935             for(var i = 0, len = arguments.length; i < len; i++) {
29936                 this.add(arguments[i]);
29937             }
29938             return null;
29939         }
29940         if(this.hasPanel(panel)){
29941             this.showPanel(panel);
29942             return panel;
29943         }
29944         var el = panel.getEl();
29945         if(el.dom.parentNode != this.mgr.el.dom){
29946             this.mgr.el.dom.appendChild(el.dom);
29947         }
29948         if(panel.setRegion){
29949             panel.setRegion(this);
29950         }
29951         this.panels.add(panel);
29952         el.setStyle("position", "absolute");
29953         if(!panel.background){
29954             this.setActivePanel(panel);
29955             if(this.config.initialSize && this.panels.getCount()==1){
29956                 this.resizeTo(this.config.initialSize);
29957             }
29958         }
29959         this.fireEvent("paneladded", this, panel);
29960         return panel;
29961     },
29962     
29963     /**
29964      * Returns true if the panel is in this region.
29965      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29966      * @return {Boolean}
29967      */
29968     hasPanel : function(panel){
29969         if(typeof panel == "object"){ // must be panel obj
29970             panel = panel.getId();
29971         }
29972         return this.getPanel(panel) ? true : false;
29973     },
29974     
29975     /**
29976      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29977      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29978      * @param {Boolean} preservePanel Overrides the config preservePanel option
29979      * @return {Roo.ContentPanel} The panel that was removed
29980      */
29981     remove : function(panel, preservePanel){
29982         panel = this.getPanel(panel);
29983         if(!panel){
29984             return null;
29985         }
29986         var e = {};
29987         this.fireEvent("beforeremove", this, panel, e);
29988         if(e.cancel === true){
29989             return null;
29990         }
29991         var panelId = panel.getId();
29992         this.panels.removeKey(panelId);
29993         return panel;
29994     },
29995     
29996     /**
29997      * Returns the panel specified or null if it's not in this region.
29998      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29999      * @return {Roo.ContentPanel}
30000      */
30001     getPanel : function(id){
30002         if(typeof id == "object"){ // must be panel obj
30003             return id;
30004         }
30005         return this.panels.get(id);
30006     },
30007     
30008     /**
30009      * Returns this regions position (north/south/east/west/center).
30010      * @return {String} 
30011      */
30012     getPosition: function(){
30013         return this.position;    
30014     }
30015 });/*
30016  * Based on:
30017  * Ext JS Library 1.1.1
30018  * Copyright(c) 2006-2007, Ext JS, LLC.
30019  *
30020  * Originally Released Under LGPL - original licence link has changed is not relivant.
30021  *
30022  * Fork - LGPL
30023  * <script type="text/javascript">
30024  */
30025  
30026 /**
30027  * @class Roo.LayoutRegion
30028  * @extends Roo.BasicLayoutRegion
30029  * This class represents a region in a layout manager.
30030  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
30031  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
30032  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
30033  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
30034  * @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})
30035  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
30036  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
30037  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
30038  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
30039  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
30040  * @cfg {String}    title           The title for the region (overrides panel titles)
30041  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
30042  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
30043  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
30044  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
30045  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
30046  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
30047  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
30048  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
30049  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
30050  * @cfg {Boolean}   showPin         True to show a pin button
30051  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
30052  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
30053  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
30054  * @cfg {Number}    width           For East/West panels
30055  * @cfg {Number}    height          For North/South panels
30056  * @cfg {Boolean}   split           To show the splitter
30057  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
30058  */
30059 Roo.LayoutRegion = function(mgr, config, pos){
30060     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
30061     var dh = Roo.DomHelper;
30062     /** This region's container element 
30063     * @type Roo.Element */
30064     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
30065     /** This region's title element 
30066     * @type Roo.Element */
30067
30068     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
30069         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
30070         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
30071     ]}, true);
30072     this.titleEl.enableDisplayMode();
30073     /** This region's title text element 
30074     * @type HTMLElement */
30075     this.titleTextEl = this.titleEl.dom.firstChild;
30076     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
30077     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
30078     this.closeBtn.enableDisplayMode();
30079     this.closeBtn.on("click", this.closeClicked, this);
30080     this.closeBtn.hide();
30081
30082     this.createBody(config);
30083     this.visible = true;
30084     this.collapsed = false;
30085
30086     if(config.hideWhenEmpty){
30087         this.hide();
30088         this.on("paneladded", this.validateVisibility, this);
30089         this.on("panelremoved", this.validateVisibility, this);
30090     }
30091     this.applyConfig(config);
30092 };
30093
30094 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
30095
30096     createBody : function(){
30097         /** This region's body element 
30098         * @type Roo.Element */
30099         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30100     },
30101
30102     applyConfig : function(c){
30103         if(c.collapsible && this.position != "center" && !this.collapsedEl){
30104             var dh = Roo.DomHelper;
30105             if(c.titlebar !== false){
30106                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30107                 this.collapseBtn.on("click", this.collapse, this);
30108                 this.collapseBtn.enableDisplayMode();
30109
30110                 if(c.showPin === true || this.showPin){
30111                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30112                     this.stickBtn.enableDisplayMode();
30113                     this.stickBtn.on("click", this.expand, this);
30114                     this.stickBtn.hide();
30115                 }
30116             }
30117             /** This region's collapsed element
30118             * @type Roo.Element */
30119             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30120                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30121             ]}, true);
30122             if(c.floatable !== false){
30123                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30124                this.collapsedEl.on("click", this.collapseClick, this);
30125             }
30126
30127             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30128                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30129                    id: "message", unselectable: "on", style:{"float":"left"}});
30130                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30131              }
30132             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30133             this.expandBtn.on("click", this.expand, this);
30134         }
30135         if(this.collapseBtn){
30136             this.collapseBtn.setVisible(c.collapsible == true);
30137         }
30138         this.cmargins = c.cmargins || this.cmargins ||
30139                          (this.position == "west" || this.position == "east" ?
30140                              {top: 0, left: 2, right:2, bottom: 0} :
30141                              {top: 2, left: 0, right:0, bottom: 2});
30142         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30143         this.bottomTabs = c.tabPosition != "top";
30144         this.autoScroll = c.autoScroll || false;
30145         if(this.autoScroll){
30146             this.bodyEl.setStyle("overflow", "auto");
30147         }else{
30148             this.bodyEl.setStyle("overflow", "hidden");
30149         }
30150         //if(c.titlebar !== false){
30151             if((!c.titlebar && !c.title) || c.titlebar === false){
30152                 this.titleEl.hide();
30153             }else{
30154                 this.titleEl.show();
30155                 if(c.title){
30156                     this.titleTextEl.innerHTML = c.title;
30157                 }
30158             }
30159         //}
30160         this.duration = c.duration || .30;
30161         this.slideDuration = c.slideDuration || .45;
30162         this.config = c;
30163         if(c.collapsed){
30164             this.collapse(true);
30165         }
30166         if(c.hidden){
30167             this.hide();
30168         }
30169     },
30170     /**
30171      * Returns true if this region is currently visible.
30172      * @return {Boolean}
30173      */
30174     isVisible : function(){
30175         return this.visible;
30176     },
30177
30178     /**
30179      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30180      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
30181      */
30182     setCollapsedTitle : function(title){
30183         title = title || "&#160;";
30184         if(this.collapsedTitleTextEl){
30185             this.collapsedTitleTextEl.innerHTML = title;
30186         }
30187     },
30188
30189     getBox : function(){
30190         var b;
30191         if(!this.collapsed){
30192             b = this.el.getBox(false, true);
30193         }else{
30194             b = this.collapsedEl.getBox(false, true);
30195         }
30196         return b;
30197     },
30198
30199     getMargins : function(){
30200         return this.collapsed ? this.cmargins : this.margins;
30201     },
30202
30203     highlight : function(){
30204         this.el.addClass("x-layout-panel-dragover");
30205     },
30206
30207     unhighlight : function(){
30208         this.el.removeClass("x-layout-panel-dragover");
30209     },
30210
30211     updateBox : function(box){
30212         this.box = box;
30213         if(!this.collapsed){
30214             this.el.dom.style.left = box.x + "px";
30215             this.el.dom.style.top = box.y + "px";
30216             this.updateBody(box.width, box.height);
30217         }else{
30218             this.collapsedEl.dom.style.left = box.x + "px";
30219             this.collapsedEl.dom.style.top = box.y + "px";
30220             this.collapsedEl.setSize(box.width, box.height);
30221         }
30222         if(this.tabs){
30223             this.tabs.autoSizeTabs();
30224         }
30225     },
30226
30227     updateBody : function(w, h){
30228         if(w !== null){
30229             this.el.setWidth(w);
30230             w -= this.el.getBorderWidth("rl");
30231             if(this.config.adjustments){
30232                 w += this.config.adjustments[0];
30233             }
30234         }
30235         if(h !== null){
30236             this.el.setHeight(h);
30237             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
30238             h -= this.el.getBorderWidth("tb");
30239             if(this.config.adjustments){
30240                 h += this.config.adjustments[1];
30241             }
30242             this.bodyEl.setHeight(h);
30243             if(this.tabs){
30244                 h = this.tabs.syncHeight(h);
30245             }
30246         }
30247         if(this.panelSize){
30248             w = w !== null ? w : this.panelSize.width;
30249             h = h !== null ? h : this.panelSize.height;
30250         }
30251         if(this.activePanel){
30252             var el = this.activePanel.getEl();
30253             w = w !== null ? w : el.getWidth();
30254             h = h !== null ? h : el.getHeight();
30255             this.panelSize = {width: w, height: h};
30256             this.activePanel.setSize(w, h);
30257         }
30258         if(Roo.isIE && this.tabs){
30259             this.tabs.el.repaint();
30260         }
30261     },
30262
30263     /**
30264      * Returns the container element for this region.
30265      * @return {Roo.Element}
30266      */
30267     getEl : function(){
30268         return this.el;
30269     },
30270
30271     /**
30272      * Hides this region.
30273      */
30274     hide : function(){
30275         if(!this.collapsed){
30276             this.el.dom.style.left = "-2000px";
30277             this.el.hide();
30278         }else{
30279             this.collapsedEl.dom.style.left = "-2000px";
30280             this.collapsedEl.hide();
30281         }
30282         this.visible = false;
30283         this.fireEvent("visibilitychange", this, false);
30284     },
30285
30286     /**
30287      * Shows this region if it was previously hidden.
30288      */
30289     show : function(){
30290         if(!this.collapsed){
30291             this.el.show();
30292         }else{
30293             this.collapsedEl.show();
30294         }
30295         this.visible = true;
30296         this.fireEvent("visibilitychange", this, true);
30297     },
30298
30299     closeClicked : function(){
30300         if(this.activePanel){
30301             this.remove(this.activePanel);
30302         }
30303     },
30304
30305     collapseClick : function(e){
30306         if(this.isSlid){
30307            e.stopPropagation();
30308            this.slideIn();
30309         }else{
30310            e.stopPropagation();
30311            this.slideOut();
30312         }
30313     },
30314
30315     /**
30316      * Collapses this region.
30317      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30318      */
30319     collapse : function(skipAnim, skipCheck){
30320         if(this.collapsed) {
30321             return;
30322         }
30323         
30324         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
30325             
30326             this.collapsed = true;
30327             if(this.split){
30328                 this.split.el.hide();
30329             }
30330             if(this.config.animate && skipAnim !== true){
30331                 this.fireEvent("invalidated", this);
30332                 this.animateCollapse();
30333             }else{
30334                 this.el.setLocation(-20000,-20000);
30335                 this.el.hide();
30336                 this.collapsedEl.show();
30337                 this.fireEvent("collapsed", this);
30338                 this.fireEvent("invalidated", this);
30339             }
30340         }
30341         
30342     },
30343
30344     animateCollapse : function(){
30345         // overridden
30346     },
30347
30348     /**
30349      * Expands this region if it was previously collapsed.
30350      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30351      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30352      */
30353     expand : function(e, skipAnim){
30354         if(e) {
30355             e.stopPropagation();
30356         }
30357         if(!this.collapsed || this.el.hasActiveFx()) {
30358             return;
30359         }
30360         if(this.isSlid){
30361             this.afterSlideIn();
30362             skipAnim = true;
30363         }
30364         this.collapsed = false;
30365         if(this.config.animate && skipAnim !== true){
30366             this.animateExpand();
30367         }else{
30368             this.el.show();
30369             if(this.split){
30370                 this.split.el.show();
30371             }
30372             this.collapsedEl.setLocation(-2000,-2000);
30373             this.collapsedEl.hide();
30374             this.fireEvent("invalidated", this);
30375             this.fireEvent("expanded", this);
30376         }
30377     },
30378
30379     animateExpand : function(){
30380         // overridden
30381     },
30382
30383     initTabs : function()
30384     {
30385         this.bodyEl.setStyle("overflow", "hidden");
30386         var ts = new Roo.TabPanel(
30387                 this.bodyEl.dom,
30388                 {
30389                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
30390                     disableTooltips: this.config.disableTabTips,
30391                     toolbar : this.config.toolbar
30392                 }
30393         );
30394         if(this.config.hideTabs){
30395             ts.stripWrap.setDisplayed(false);
30396         }
30397         this.tabs = ts;
30398         ts.resizeTabs = this.config.resizeTabs === true;
30399         ts.minTabWidth = this.config.minTabWidth || 40;
30400         ts.maxTabWidth = this.config.maxTabWidth || 250;
30401         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30402         ts.monitorResize = false;
30403         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30404         ts.bodyEl.addClass('x-layout-tabs-body');
30405         this.panels.each(this.initPanelAsTab, this);
30406     },
30407
30408     initPanelAsTab : function(panel){
30409         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30410                     this.config.closeOnTab && panel.isClosable());
30411         if(panel.tabTip !== undefined){
30412             ti.setTooltip(panel.tabTip);
30413         }
30414         ti.on("activate", function(){
30415               this.setActivePanel(panel);
30416         }, this);
30417         if(this.config.closeOnTab){
30418             ti.on("beforeclose", function(t, e){
30419                 e.cancel = true;
30420                 this.remove(panel);
30421             }, this);
30422         }
30423         return ti;
30424     },
30425
30426     updatePanelTitle : function(panel, title){
30427         if(this.activePanel == panel){
30428             this.updateTitle(title);
30429         }
30430         if(this.tabs){
30431             var ti = this.tabs.getTab(panel.getEl().id);
30432             ti.setText(title);
30433             if(panel.tabTip !== undefined){
30434                 ti.setTooltip(panel.tabTip);
30435             }
30436         }
30437     },
30438
30439     updateTitle : function(title){
30440         if(this.titleTextEl && !this.config.title){
30441             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30442         }
30443     },
30444
30445     setActivePanel : function(panel){
30446         panel = this.getPanel(panel);
30447         if(this.activePanel && this.activePanel != panel){
30448             this.activePanel.setActiveState(false);
30449         }
30450         this.activePanel = panel;
30451         panel.setActiveState(true);
30452         if(this.panelSize){
30453             panel.setSize(this.panelSize.width, this.panelSize.height);
30454         }
30455         if(this.closeBtn){
30456             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30457         }
30458         this.updateTitle(panel.getTitle());
30459         if(this.tabs){
30460             this.fireEvent("invalidated", this);
30461         }
30462         this.fireEvent("panelactivated", this, panel);
30463     },
30464
30465     /**
30466      * Shows the specified panel.
30467      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30468      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30469      */
30470     showPanel : function(panel)
30471     {
30472         panel = this.getPanel(panel);
30473         if(panel){
30474             if(this.tabs){
30475                 var tab = this.tabs.getTab(panel.getEl().id);
30476                 if(tab.isHidden()){
30477                     this.tabs.unhideTab(tab.id);
30478                 }
30479                 tab.activate();
30480             }else{
30481                 this.setActivePanel(panel);
30482             }
30483         }
30484         return panel;
30485     },
30486
30487     /**
30488      * Get the active panel for this region.
30489      * @return {Roo.ContentPanel} The active panel or null
30490      */
30491     getActivePanel : function(){
30492         return this.activePanel;
30493     },
30494
30495     validateVisibility : function(){
30496         if(this.panels.getCount() < 1){
30497             this.updateTitle("&#160;");
30498             this.closeBtn.hide();
30499             this.hide();
30500         }else{
30501             if(!this.isVisible()){
30502                 this.show();
30503             }
30504         }
30505     },
30506
30507     /**
30508      * Adds the passed ContentPanel(s) to this region.
30509      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30510      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30511      */
30512     add : function(panel){
30513         if(arguments.length > 1){
30514             for(var i = 0, len = arguments.length; i < len; i++) {
30515                 this.add(arguments[i]);
30516             }
30517             return null;
30518         }
30519         if(this.hasPanel(panel)){
30520             this.showPanel(panel);
30521             return panel;
30522         }
30523         panel.setRegion(this);
30524         this.panels.add(panel);
30525         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30526             this.bodyEl.dom.appendChild(panel.getEl().dom);
30527             if(panel.background !== true){
30528                 this.setActivePanel(panel);
30529             }
30530             this.fireEvent("paneladded", this, panel);
30531             return panel;
30532         }
30533         if(!this.tabs){
30534             this.initTabs();
30535         }else{
30536             this.initPanelAsTab(panel);
30537         }
30538         if(panel.background !== true){
30539             this.tabs.activate(panel.getEl().id);
30540         }
30541         this.fireEvent("paneladded", this, panel);
30542         return panel;
30543     },
30544
30545     /**
30546      * Hides the tab for the specified panel.
30547      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30548      */
30549     hidePanel : function(panel){
30550         if(this.tabs && (panel = this.getPanel(panel))){
30551             this.tabs.hideTab(panel.getEl().id);
30552         }
30553     },
30554
30555     /**
30556      * Unhides the tab for a previously hidden panel.
30557      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30558      */
30559     unhidePanel : function(panel){
30560         if(this.tabs && (panel = this.getPanel(panel))){
30561             this.tabs.unhideTab(panel.getEl().id);
30562         }
30563     },
30564
30565     clearPanels : function(){
30566         while(this.panels.getCount() > 0){
30567              this.remove(this.panels.first());
30568         }
30569     },
30570
30571     /**
30572      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30573      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30574      * @param {Boolean} preservePanel Overrides the config preservePanel option
30575      * @return {Roo.ContentPanel} The panel that was removed
30576      */
30577     remove : function(panel, preservePanel){
30578         panel = this.getPanel(panel);
30579         if(!panel){
30580             return null;
30581         }
30582         var e = {};
30583         this.fireEvent("beforeremove", this, panel, e);
30584         if(e.cancel === true){
30585             return null;
30586         }
30587         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30588         var panelId = panel.getId();
30589         this.panels.removeKey(panelId);
30590         if(preservePanel){
30591             document.body.appendChild(panel.getEl().dom);
30592         }
30593         if(this.tabs){
30594             this.tabs.removeTab(panel.getEl().id);
30595         }else if (!preservePanel){
30596             this.bodyEl.dom.removeChild(panel.getEl().dom);
30597         }
30598         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30599             var p = this.panels.first();
30600             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30601             tempEl.appendChild(p.getEl().dom);
30602             this.bodyEl.update("");
30603             this.bodyEl.dom.appendChild(p.getEl().dom);
30604             tempEl = null;
30605             this.updateTitle(p.getTitle());
30606             this.tabs = null;
30607             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30608             this.setActivePanel(p);
30609         }
30610         panel.setRegion(null);
30611         if(this.activePanel == panel){
30612             this.activePanel = null;
30613         }
30614         if(this.config.autoDestroy !== false && preservePanel !== true){
30615             try{panel.destroy();}catch(e){}
30616         }
30617         this.fireEvent("panelremoved", this, panel);
30618         return panel;
30619     },
30620
30621     /**
30622      * Returns the TabPanel component used by this region
30623      * @return {Roo.TabPanel}
30624      */
30625     getTabs : function(){
30626         return this.tabs;
30627     },
30628
30629     createTool : function(parentEl, className){
30630         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30631             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30632         btn.addClassOnOver("x-layout-tools-button-over");
30633         return btn;
30634     }
30635 });/*
30636  * Based on:
30637  * Ext JS Library 1.1.1
30638  * Copyright(c) 2006-2007, Ext JS, LLC.
30639  *
30640  * Originally Released Under LGPL - original licence link has changed is not relivant.
30641  *
30642  * Fork - LGPL
30643  * <script type="text/javascript">
30644  */
30645  
30646
30647
30648 /**
30649  * @class Roo.SplitLayoutRegion
30650  * @extends Roo.LayoutRegion
30651  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30652  */
30653 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30654     this.cursor = cursor;
30655     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30656 };
30657
30658 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30659     splitTip : "Drag to resize.",
30660     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30661     useSplitTips : false,
30662
30663     applyConfig : function(config){
30664         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30665         if(config.split){
30666             if(!this.split){
30667                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30668                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30669                 /** The SplitBar for this region 
30670                 * @type Roo.SplitBar */
30671                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30672                 this.split.on("moved", this.onSplitMove, this);
30673                 this.split.useShim = config.useShim === true;
30674                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30675                 if(this.useSplitTips){
30676                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30677                 }
30678                 if(config.collapsible){
30679                     this.split.el.on("dblclick", this.collapse,  this);
30680                 }
30681             }
30682             if(typeof config.minSize != "undefined"){
30683                 this.split.minSize = config.minSize;
30684             }
30685             if(typeof config.maxSize != "undefined"){
30686                 this.split.maxSize = config.maxSize;
30687             }
30688             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30689                 this.hideSplitter();
30690             }
30691         }
30692     },
30693
30694     getHMaxSize : function(){
30695          var cmax = this.config.maxSize || 10000;
30696          var center = this.mgr.getRegion("center");
30697          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30698     },
30699
30700     getVMaxSize : function(){
30701          var cmax = this.config.maxSize || 10000;
30702          var center = this.mgr.getRegion("center");
30703          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30704     },
30705
30706     onSplitMove : function(split, newSize){
30707         this.fireEvent("resized", this, newSize);
30708     },
30709     
30710     /** 
30711      * Returns the {@link Roo.SplitBar} for this region.
30712      * @return {Roo.SplitBar}
30713      */
30714     getSplitBar : function(){
30715         return this.split;
30716     },
30717     
30718     hide : function(){
30719         this.hideSplitter();
30720         Roo.SplitLayoutRegion.superclass.hide.call(this);
30721     },
30722
30723     hideSplitter : function(){
30724         if(this.split){
30725             this.split.el.setLocation(-2000,-2000);
30726             this.split.el.hide();
30727         }
30728     },
30729
30730     show : function(){
30731         if(this.split){
30732             this.split.el.show();
30733         }
30734         Roo.SplitLayoutRegion.superclass.show.call(this);
30735     },
30736     
30737     beforeSlide: function(){
30738         if(Roo.isGecko){// firefox overflow auto bug workaround
30739             this.bodyEl.clip();
30740             if(this.tabs) {
30741                 this.tabs.bodyEl.clip();
30742             }
30743             if(this.activePanel){
30744                 this.activePanel.getEl().clip();
30745                 
30746                 if(this.activePanel.beforeSlide){
30747                     this.activePanel.beforeSlide();
30748                 }
30749             }
30750         }
30751     },
30752     
30753     afterSlide : function(){
30754         if(Roo.isGecko){// firefox overflow auto bug workaround
30755             this.bodyEl.unclip();
30756             if(this.tabs) {
30757                 this.tabs.bodyEl.unclip();
30758             }
30759             if(this.activePanel){
30760                 this.activePanel.getEl().unclip();
30761                 if(this.activePanel.afterSlide){
30762                     this.activePanel.afterSlide();
30763                 }
30764             }
30765         }
30766     },
30767
30768     initAutoHide : function(){
30769         if(this.autoHide !== false){
30770             if(!this.autoHideHd){
30771                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30772                 this.autoHideHd = {
30773                     "mouseout": function(e){
30774                         if(!e.within(this.el, true)){
30775                             st.delay(500);
30776                         }
30777                     },
30778                     "mouseover" : function(e){
30779                         st.cancel();
30780                     },
30781                     scope : this
30782                 };
30783             }
30784             this.el.on(this.autoHideHd);
30785         }
30786     },
30787
30788     clearAutoHide : function(){
30789         if(this.autoHide !== false){
30790             this.el.un("mouseout", this.autoHideHd.mouseout);
30791             this.el.un("mouseover", this.autoHideHd.mouseover);
30792         }
30793     },
30794
30795     clearMonitor : function(){
30796         Roo.get(document).un("click", this.slideInIf, this);
30797     },
30798
30799     // these names are backwards but not changed for compat
30800     slideOut : function(){
30801         if(this.isSlid || this.el.hasActiveFx()){
30802             return;
30803         }
30804         this.isSlid = true;
30805         if(this.collapseBtn){
30806             this.collapseBtn.hide();
30807         }
30808         this.closeBtnState = this.closeBtn.getStyle('display');
30809         this.closeBtn.hide();
30810         if(this.stickBtn){
30811             this.stickBtn.show();
30812         }
30813         this.el.show();
30814         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30815         this.beforeSlide();
30816         this.el.setStyle("z-index", 10001);
30817         this.el.slideIn(this.getSlideAnchor(), {
30818             callback: function(){
30819                 this.afterSlide();
30820                 this.initAutoHide();
30821                 Roo.get(document).on("click", this.slideInIf, this);
30822                 this.fireEvent("slideshow", this);
30823             },
30824             scope: this,
30825             block: true
30826         });
30827     },
30828
30829     afterSlideIn : function(){
30830         this.clearAutoHide();
30831         this.isSlid = false;
30832         this.clearMonitor();
30833         this.el.setStyle("z-index", "");
30834         if(this.collapseBtn){
30835             this.collapseBtn.show();
30836         }
30837         this.closeBtn.setStyle('display', this.closeBtnState);
30838         if(this.stickBtn){
30839             this.stickBtn.hide();
30840         }
30841         this.fireEvent("slidehide", this);
30842     },
30843
30844     slideIn : function(cb){
30845         if(!this.isSlid || this.el.hasActiveFx()){
30846             Roo.callback(cb);
30847             return;
30848         }
30849         this.isSlid = false;
30850         this.beforeSlide();
30851         this.el.slideOut(this.getSlideAnchor(), {
30852             callback: function(){
30853                 this.el.setLeftTop(-10000, -10000);
30854                 this.afterSlide();
30855                 this.afterSlideIn();
30856                 Roo.callback(cb);
30857             },
30858             scope: this,
30859             block: true
30860         });
30861     },
30862     
30863     slideInIf : function(e){
30864         if(!e.within(this.el)){
30865             this.slideIn();
30866         }
30867     },
30868
30869     animateCollapse : function(){
30870         this.beforeSlide();
30871         this.el.setStyle("z-index", 20000);
30872         var anchor = this.getSlideAnchor();
30873         this.el.slideOut(anchor, {
30874             callback : function(){
30875                 this.el.setStyle("z-index", "");
30876                 this.collapsedEl.slideIn(anchor, {duration:.3});
30877                 this.afterSlide();
30878                 this.el.setLocation(-10000,-10000);
30879                 this.el.hide();
30880                 this.fireEvent("collapsed", this);
30881             },
30882             scope: this,
30883             block: true
30884         });
30885     },
30886
30887     animateExpand : function(){
30888         this.beforeSlide();
30889         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30890         this.el.setStyle("z-index", 20000);
30891         this.collapsedEl.hide({
30892             duration:.1
30893         });
30894         this.el.slideIn(this.getSlideAnchor(), {
30895             callback : function(){
30896                 this.el.setStyle("z-index", "");
30897                 this.afterSlide();
30898                 if(this.split){
30899                     this.split.el.show();
30900                 }
30901                 this.fireEvent("invalidated", this);
30902                 this.fireEvent("expanded", this);
30903             },
30904             scope: this,
30905             block: true
30906         });
30907     },
30908
30909     anchors : {
30910         "west" : "left",
30911         "east" : "right",
30912         "north" : "top",
30913         "south" : "bottom"
30914     },
30915
30916     sanchors : {
30917         "west" : "l",
30918         "east" : "r",
30919         "north" : "t",
30920         "south" : "b"
30921     },
30922
30923     canchors : {
30924         "west" : "tl-tr",
30925         "east" : "tr-tl",
30926         "north" : "tl-bl",
30927         "south" : "bl-tl"
30928     },
30929
30930     getAnchor : function(){
30931         return this.anchors[this.position];
30932     },
30933
30934     getCollapseAnchor : function(){
30935         return this.canchors[this.position];
30936     },
30937
30938     getSlideAnchor : function(){
30939         return this.sanchors[this.position];
30940     },
30941
30942     getAlignAdj : function(){
30943         var cm = this.cmargins;
30944         switch(this.position){
30945             case "west":
30946                 return [0, 0];
30947             break;
30948             case "east":
30949                 return [0, 0];
30950             break;
30951             case "north":
30952                 return [0, 0];
30953             break;
30954             case "south":
30955                 return [0, 0];
30956             break;
30957         }
30958     },
30959
30960     getExpandAdj : function(){
30961         var c = this.collapsedEl, cm = this.cmargins;
30962         switch(this.position){
30963             case "west":
30964                 return [-(cm.right+c.getWidth()+cm.left), 0];
30965             break;
30966             case "east":
30967                 return [cm.right+c.getWidth()+cm.left, 0];
30968             break;
30969             case "north":
30970                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30971             break;
30972             case "south":
30973                 return [0, cm.top+cm.bottom+c.getHeight()];
30974             break;
30975         }
30976     }
30977 });/*
30978  * Based on:
30979  * Ext JS Library 1.1.1
30980  * Copyright(c) 2006-2007, Ext JS, LLC.
30981  *
30982  * Originally Released Under LGPL - original licence link has changed is not relivant.
30983  *
30984  * Fork - LGPL
30985  * <script type="text/javascript">
30986  */
30987 /*
30988  * These classes are private internal classes
30989  */
30990 Roo.CenterLayoutRegion = function(mgr, config){
30991     Roo.LayoutRegion.call(this, mgr, config, "center");
30992     this.visible = true;
30993     this.minWidth = config.minWidth || 20;
30994     this.minHeight = config.minHeight || 20;
30995 };
30996
30997 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30998     hide : function(){
30999         // center panel can't be hidden
31000     },
31001     
31002     show : function(){
31003         // center panel can't be hidden
31004     },
31005     
31006     getMinWidth: function(){
31007         return this.minWidth;
31008     },
31009     
31010     getMinHeight: function(){
31011         return this.minHeight;
31012     }
31013 });
31014
31015
31016 Roo.NorthLayoutRegion = function(mgr, config){
31017     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
31018     if(this.split){
31019         this.split.placement = Roo.SplitBar.TOP;
31020         this.split.orientation = Roo.SplitBar.VERTICAL;
31021         this.split.el.addClass("x-layout-split-v");
31022     }
31023     var size = config.initialSize || config.height;
31024     if(typeof size != "undefined"){
31025         this.el.setHeight(size);
31026     }
31027 };
31028 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
31029     orientation: Roo.SplitBar.VERTICAL,
31030     getBox : function(){
31031         if(this.collapsed){
31032             return this.collapsedEl.getBox();
31033         }
31034         var box = this.el.getBox();
31035         if(this.split){
31036             box.height += this.split.el.getHeight();
31037         }
31038         return box;
31039     },
31040     
31041     updateBox : function(box){
31042         if(this.split && !this.collapsed){
31043             box.height -= this.split.el.getHeight();
31044             this.split.el.setLeft(box.x);
31045             this.split.el.setTop(box.y+box.height);
31046             this.split.el.setWidth(box.width);
31047         }
31048         if(this.collapsed){
31049             this.updateBody(box.width, null);
31050         }
31051         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31052     }
31053 });
31054
31055 Roo.SouthLayoutRegion = function(mgr, config){
31056     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
31057     if(this.split){
31058         this.split.placement = Roo.SplitBar.BOTTOM;
31059         this.split.orientation = Roo.SplitBar.VERTICAL;
31060         this.split.el.addClass("x-layout-split-v");
31061     }
31062     var size = config.initialSize || config.height;
31063     if(typeof size != "undefined"){
31064         this.el.setHeight(size);
31065     }
31066 };
31067 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
31068     orientation: Roo.SplitBar.VERTICAL,
31069     getBox : function(){
31070         if(this.collapsed){
31071             return this.collapsedEl.getBox();
31072         }
31073         var box = this.el.getBox();
31074         if(this.split){
31075             var sh = this.split.el.getHeight();
31076             box.height += sh;
31077             box.y -= sh;
31078         }
31079         return box;
31080     },
31081     
31082     updateBox : function(box){
31083         if(this.split && !this.collapsed){
31084             var sh = this.split.el.getHeight();
31085             box.height -= sh;
31086             box.y += sh;
31087             this.split.el.setLeft(box.x);
31088             this.split.el.setTop(box.y-sh);
31089             this.split.el.setWidth(box.width);
31090         }
31091         if(this.collapsed){
31092             this.updateBody(box.width, null);
31093         }
31094         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31095     }
31096 });
31097
31098 Roo.EastLayoutRegion = function(mgr, config){
31099     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31100     if(this.split){
31101         this.split.placement = Roo.SplitBar.RIGHT;
31102         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31103         this.split.el.addClass("x-layout-split-h");
31104     }
31105     var size = config.initialSize || config.width;
31106     if(typeof size != "undefined"){
31107         this.el.setWidth(size);
31108     }
31109 };
31110 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31111     orientation: Roo.SplitBar.HORIZONTAL,
31112     getBox : function(){
31113         if(this.collapsed){
31114             return this.collapsedEl.getBox();
31115         }
31116         var box = this.el.getBox();
31117         if(this.split){
31118             var sw = this.split.el.getWidth();
31119             box.width += sw;
31120             box.x -= sw;
31121         }
31122         return box;
31123     },
31124
31125     updateBox : function(box){
31126         if(this.split && !this.collapsed){
31127             var sw = this.split.el.getWidth();
31128             box.width -= sw;
31129             this.split.el.setLeft(box.x);
31130             this.split.el.setTop(box.y);
31131             this.split.el.setHeight(box.height);
31132             box.x += sw;
31133         }
31134         if(this.collapsed){
31135             this.updateBody(null, box.height);
31136         }
31137         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31138     }
31139 });
31140
31141 Roo.WestLayoutRegion = function(mgr, config){
31142     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31143     if(this.split){
31144         this.split.placement = Roo.SplitBar.LEFT;
31145         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31146         this.split.el.addClass("x-layout-split-h");
31147     }
31148     var size = config.initialSize || config.width;
31149     if(typeof size != "undefined"){
31150         this.el.setWidth(size);
31151     }
31152 };
31153 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31154     orientation: Roo.SplitBar.HORIZONTAL,
31155     getBox : function(){
31156         if(this.collapsed){
31157             return this.collapsedEl.getBox();
31158         }
31159         var box = this.el.getBox();
31160         if(this.split){
31161             box.width += this.split.el.getWidth();
31162         }
31163         return box;
31164     },
31165     
31166     updateBox : function(box){
31167         if(this.split && !this.collapsed){
31168             var sw = this.split.el.getWidth();
31169             box.width -= sw;
31170             this.split.el.setLeft(box.x+box.width);
31171             this.split.el.setTop(box.y);
31172             this.split.el.setHeight(box.height);
31173         }
31174         if(this.collapsed){
31175             this.updateBody(null, box.height);
31176         }
31177         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31178     }
31179 });
31180 /*
31181  * Based on:
31182  * Ext JS Library 1.1.1
31183  * Copyright(c) 2006-2007, Ext JS, LLC.
31184  *
31185  * Originally Released Under LGPL - original licence link has changed is not relivant.
31186  *
31187  * Fork - LGPL
31188  * <script type="text/javascript">
31189  */
31190  
31191  
31192 /*
31193  * Private internal class for reading and applying state
31194  */
31195 Roo.LayoutStateManager = function(layout){
31196      // default empty state
31197      this.state = {
31198         north: {},
31199         south: {},
31200         east: {},
31201         west: {}       
31202     };
31203 };
31204
31205 Roo.LayoutStateManager.prototype = {
31206     init : function(layout, provider){
31207         this.provider = provider;
31208         var state = provider.get(layout.id+"-layout-state");
31209         if(state){
31210             var wasUpdating = layout.isUpdating();
31211             if(!wasUpdating){
31212                 layout.beginUpdate();
31213             }
31214             for(var key in state){
31215                 if(typeof state[key] != "function"){
31216                     var rstate = state[key];
31217                     var r = layout.getRegion(key);
31218                     if(r && rstate){
31219                         if(rstate.size){
31220                             r.resizeTo(rstate.size);
31221                         }
31222                         if(rstate.collapsed == true){
31223                             r.collapse(true);
31224                         }else{
31225                             r.expand(null, true);
31226                         }
31227                     }
31228                 }
31229             }
31230             if(!wasUpdating){
31231                 layout.endUpdate();
31232             }
31233             this.state = state; 
31234         }
31235         this.layout = layout;
31236         layout.on("regionresized", this.onRegionResized, this);
31237         layout.on("regioncollapsed", this.onRegionCollapsed, this);
31238         layout.on("regionexpanded", this.onRegionExpanded, this);
31239     },
31240     
31241     storeState : function(){
31242         this.provider.set(this.layout.id+"-layout-state", this.state);
31243     },
31244     
31245     onRegionResized : function(region, newSize){
31246         this.state[region.getPosition()].size = newSize;
31247         this.storeState();
31248     },
31249     
31250     onRegionCollapsed : function(region){
31251         this.state[region.getPosition()].collapsed = true;
31252         this.storeState();
31253     },
31254     
31255     onRegionExpanded : function(region){
31256         this.state[region.getPosition()].collapsed = false;
31257         this.storeState();
31258     }
31259 };/*
31260  * Based on:
31261  * Ext JS Library 1.1.1
31262  * Copyright(c) 2006-2007, Ext JS, LLC.
31263  *
31264  * Originally Released Under LGPL - original licence link has changed is not relivant.
31265  *
31266  * Fork - LGPL
31267  * <script type="text/javascript">
31268  */
31269 /**
31270  * @class Roo.ContentPanel
31271  * @extends Roo.util.Observable
31272  * @children Roo.form.Form Roo.JsonView Roo.View
31273  * A basic ContentPanel element.
31274  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
31275  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
31276  * @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
31277  * @cfg {Boolean}   closable      True if the panel can be closed/removed
31278  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
31279  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
31280  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
31281  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
31282  * @cfg {String} title          The title for this panel
31283  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
31284  * @cfg {String} url            Calls {@link #setUrl} with this value
31285  * @cfg {String} region [required]   (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
31286  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
31287  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
31288  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
31289  * @cfg {String}    style  Extra style to add to the content panel
31290  * @cfg {Roo.menu.Menu} menu  popup menu
31291
31292  * @constructor
31293  * Create a new ContentPanel.
31294  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
31295  * @param {String/Object} config A string to set only the title or a config object
31296  * @param {String} content (optional) Set the HTML content for this panel
31297  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
31298  */
31299 Roo.ContentPanel = function(el, config, content){
31300     
31301      
31302     /*
31303     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
31304         config = el;
31305         el = Roo.id();
31306     }
31307     if (config && config.parentLayout) { 
31308         el = config.parentLayout.el.createChild(); 
31309     }
31310     */
31311     if(el.autoCreate){ // xtype is available if this is called from factory
31312         config = el;
31313         el = Roo.id();
31314     }
31315     this.el = Roo.get(el);
31316     if(!this.el && config && config.autoCreate){
31317         if(typeof config.autoCreate == "object"){
31318             if(!config.autoCreate.id){
31319                 config.autoCreate.id = config.id||el;
31320             }
31321             this.el = Roo.DomHelper.append(document.body,
31322                         config.autoCreate, true);
31323         }else{
31324             this.el = Roo.DomHelper.append(document.body,
31325                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
31326         }
31327     }
31328     
31329     
31330     this.closable = false;
31331     this.loaded = false;
31332     this.active = false;
31333     if(typeof config == "string"){
31334         this.title = config;
31335     }else{
31336         Roo.apply(this, config);
31337     }
31338     
31339     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31340         this.wrapEl = this.el.wrap();
31341         this.toolbar.container = this.el.insertSibling(false, 'before');
31342         this.toolbar = new Roo.Toolbar(this.toolbar);
31343     }
31344     
31345     // xtype created footer. - not sure if will work as we normally have to render first..
31346     if (this.footer && !this.footer.el && this.footer.xtype) {
31347         if (!this.wrapEl) {
31348             this.wrapEl = this.el.wrap();
31349         }
31350     
31351         this.footer.container = this.wrapEl.createChild();
31352          
31353         this.footer = Roo.factory(this.footer, Roo);
31354         
31355     }
31356     
31357     if(this.resizeEl){
31358         this.resizeEl = Roo.get(this.resizeEl, true);
31359     }else{
31360         this.resizeEl = this.el;
31361     }
31362     // handle view.xtype
31363     
31364  
31365     
31366     
31367     this.addEvents({
31368         /**
31369          * @event activate
31370          * Fires when this panel is activated. 
31371          * @param {Roo.ContentPanel} this
31372          */
31373         "activate" : true,
31374         /**
31375          * @event deactivate
31376          * Fires when this panel is activated. 
31377          * @param {Roo.ContentPanel} this
31378          */
31379         "deactivate" : true,
31380
31381         /**
31382          * @event resize
31383          * Fires when this panel is resized if fitToFrame is true.
31384          * @param {Roo.ContentPanel} this
31385          * @param {Number} width The width after any component adjustments
31386          * @param {Number} height The height after any component adjustments
31387          */
31388         "resize" : true,
31389         
31390          /**
31391          * @event render
31392          * Fires when this tab is created
31393          * @param {Roo.ContentPanel} this
31394          */
31395         "render" : true
31396          
31397         
31398     });
31399     
31400
31401     
31402     
31403     if(this.autoScroll){
31404         this.resizeEl.setStyle("overflow", "auto");
31405     } else {
31406         // fix randome scrolling
31407         this.el.on('scroll', function() {
31408             Roo.log('fix random scolling');
31409             this.scrollTo('top',0); 
31410         });
31411     }
31412     content = content || this.content;
31413     if(content){
31414         this.setContent(content);
31415     }
31416     if(config && config.url){
31417         this.setUrl(this.url, this.params, this.loadOnce);
31418     }
31419     
31420     
31421     
31422     Roo.ContentPanel.superclass.constructor.call(this);
31423     
31424     if (this.view && typeof(this.view.xtype) != 'undefined') {
31425         this.view.el = this.el.appendChild(document.createElement("div"));
31426         this.view = Roo.factory(this.view); 
31427         this.view.render  &&  this.view.render(false, '');  
31428     }
31429     
31430     
31431     this.fireEvent('render', this);
31432 };
31433
31434 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31435     tabTip:'',
31436     setRegion : function(region){
31437         this.region = region;
31438         if(region){
31439            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31440         }else{
31441            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31442         } 
31443     },
31444     
31445     /**
31446      * Returns the toolbar for this Panel if one was configured. 
31447      * @return {Roo.Toolbar} 
31448      */
31449     getToolbar : function(){
31450         return this.toolbar;
31451     },
31452     
31453     setActiveState : function(active){
31454         this.active = active;
31455         if(!active){
31456             this.fireEvent("deactivate", this);
31457         }else{
31458             this.fireEvent("activate", this);
31459         }
31460     },
31461     /**
31462      * Updates this panel's element
31463      * @param {String} content The new content
31464      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31465     */
31466     setContent : function(content, loadScripts){
31467         this.el.update(content, loadScripts);
31468     },
31469
31470     ignoreResize : function(w, h){
31471         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31472             return true;
31473         }else{
31474             this.lastSize = {width: w, height: h};
31475             return false;
31476         }
31477     },
31478     /**
31479      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31480      * @return {Roo.UpdateManager} The UpdateManager
31481      */
31482     getUpdateManager : function(){
31483         return this.el.getUpdateManager();
31484     },
31485      /**
31486      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31487      * @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:
31488 <pre><code>
31489 panel.load({
31490     url: "your-url.php",
31491     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31492     callback: yourFunction,
31493     scope: yourObject, //(optional scope)
31494     discardUrl: false,
31495     nocache: false,
31496     text: "Loading...",
31497     timeout: 30,
31498     scripts: false
31499 });
31500 </code></pre>
31501      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31502      * 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.
31503      * @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}
31504      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31505      * @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.
31506      * @return {Roo.ContentPanel} this
31507      */
31508     load : function(){
31509         var um = this.el.getUpdateManager();
31510         um.update.apply(um, arguments);
31511         return this;
31512     },
31513
31514
31515     /**
31516      * 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.
31517      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31518      * @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)
31519      * @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)
31520      * @return {Roo.UpdateManager} The UpdateManager
31521      */
31522     setUrl : function(url, params, loadOnce){
31523         if(this.refreshDelegate){
31524             this.removeListener("activate", this.refreshDelegate);
31525         }
31526         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31527         this.on("activate", this.refreshDelegate);
31528         return this.el.getUpdateManager();
31529     },
31530     
31531     _handleRefresh : function(url, params, loadOnce){
31532         if(!loadOnce || !this.loaded){
31533             var updater = this.el.getUpdateManager();
31534             updater.update(url, params, this._setLoaded.createDelegate(this));
31535         }
31536     },
31537     
31538     _setLoaded : function(){
31539         this.loaded = true;
31540     }, 
31541     
31542     /**
31543      * Returns this panel's id
31544      * @return {String} 
31545      */
31546     getId : function(){
31547         return this.el.id;
31548     },
31549     
31550     /** 
31551      * Returns this panel's element - used by regiosn to add.
31552      * @return {Roo.Element} 
31553      */
31554     getEl : function(){
31555         return this.wrapEl || this.el;
31556     },
31557     
31558     adjustForComponents : function(width, height)
31559     {
31560         //Roo.log('adjustForComponents ');
31561         if(this.resizeEl != this.el){
31562             width -= this.el.getFrameWidth('lr');
31563             height -= this.el.getFrameWidth('tb');
31564         }
31565         if(this.toolbar){
31566             var te = this.toolbar.getEl();
31567             height -= te.getHeight();
31568             te.setWidth(width);
31569         }
31570         if(this.footer){
31571             var te = this.footer.getEl();
31572             //Roo.log("footer:" + te.getHeight());
31573             
31574             height -= te.getHeight();
31575             te.setWidth(width);
31576         }
31577         
31578         
31579         if(this.adjustments){
31580             width += this.adjustments[0];
31581             height += this.adjustments[1];
31582         }
31583         return {"width": width, "height": height};
31584     },
31585     
31586     setSize : function(width, height){
31587         if(this.fitToFrame && !this.ignoreResize(width, height)){
31588             if(this.fitContainer && this.resizeEl != this.el){
31589                 this.el.setSize(width, height);
31590             }
31591             var size = this.adjustForComponents(width, height);
31592             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31593             this.fireEvent('resize', this, size.width, size.height);
31594         }
31595     },
31596     
31597     /**
31598      * Returns this panel's title
31599      * @return {String} 
31600      */
31601     getTitle : function(){
31602         return this.title;
31603     },
31604     
31605     /**
31606      * Set this panel's title
31607      * @param {String} title
31608      */
31609     setTitle : function(title){
31610         this.title = title;
31611         if(this.region){
31612             this.region.updatePanelTitle(this, title);
31613         }
31614     },
31615     
31616     /**
31617      * Returns true is this panel was configured to be closable
31618      * @return {Boolean} 
31619      */
31620     isClosable : function(){
31621         return this.closable;
31622     },
31623     
31624     beforeSlide : function(){
31625         this.el.clip();
31626         this.resizeEl.clip();
31627     },
31628     
31629     afterSlide : function(){
31630         this.el.unclip();
31631         this.resizeEl.unclip();
31632     },
31633     
31634     /**
31635      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31636      *   Will fail silently if the {@link #setUrl} method has not been called.
31637      *   This does not activate the panel, just updates its content.
31638      */
31639     refresh : function(){
31640         if(this.refreshDelegate){
31641            this.loaded = false;
31642            this.refreshDelegate();
31643         }
31644     },
31645     
31646     /**
31647      * Destroys this panel
31648      */
31649     destroy : function(){
31650         this.el.removeAllListeners();
31651         var tempEl = document.createElement("span");
31652         tempEl.appendChild(this.el.dom);
31653         tempEl.innerHTML = "";
31654         this.el.remove();
31655         this.el = null;
31656     },
31657     
31658     /**
31659      * form - if the content panel contains a form - this is a reference to it.
31660      * @type {Roo.form.Form}
31661      */
31662     form : false,
31663     /**
31664      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31665      *    This contains a reference to it.
31666      * @type {Roo.View}
31667      */
31668     view : false,
31669     
31670       /**
31671      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31672      * <pre><code>
31673
31674 layout.addxtype({
31675        xtype : 'Form',
31676        items: [ .... ]
31677    }
31678 );
31679
31680 </code></pre>
31681      * @param {Object} cfg Xtype definition of item to add.
31682      */
31683     
31684     addxtype : function(cfg) {
31685         // add form..
31686         if (cfg.xtype.match(/^Form$/)) {
31687             
31688             var el;
31689             //if (this.footer) {
31690             //    el = this.footer.container.insertSibling(false, 'before');
31691             //} else {
31692                 el = this.el.createChild();
31693             //}
31694
31695             this.form = new  Roo.form.Form(cfg);
31696             
31697             
31698             if ( this.form.allItems.length) {
31699                 this.form.render(el.dom);
31700             }
31701             return this.form;
31702         }
31703         // should only have one of theses..
31704         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31705             // views.. should not be just added - used named prop 'view''
31706             
31707             cfg.el = this.el.appendChild(document.createElement("div"));
31708             // factory?
31709             
31710             var ret = new Roo.factory(cfg);
31711              
31712              ret.render && ret.render(false, ''); // render blank..
31713             this.view = ret;
31714             return ret;
31715         }
31716         return false;
31717     }
31718 });
31719
31720 /**
31721  * @class Roo.GridPanel
31722  * @extends Roo.ContentPanel
31723  * @constructor
31724  * Create a new GridPanel.
31725  * @param {Roo.grid.Grid} grid The grid for this panel
31726  * @param {String/Object} config A string to set only the panel's title, or a config object
31727  */
31728 Roo.GridPanel = function(grid, config){
31729     
31730   
31731     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31732         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31733         
31734     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31735     
31736     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31737     
31738     if(this.toolbar){
31739         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31740     }
31741     // xtype created footer. - not sure if will work as we normally have to render first..
31742     if (this.footer && !this.footer.el && this.footer.xtype) {
31743         
31744         this.footer.container = this.grid.getView().getFooterPanel(true);
31745         this.footer.dataSource = this.grid.dataSource;
31746         this.footer = Roo.factory(this.footer, Roo);
31747         
31748     }
31749     
31750     grid.monitorWindowResize = false; // turn off autosizing
31751     grid.autoHeight = false;
31752     grid.autoWidth = false;
31753     this.grid = grid;
31754     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31755 };
31756
31757 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31758     getId : function(){
31759         return this.grid.id;
31760     },
31761     
31762     /**
31763      * Returns the grid for this panel
31764      * @return {Roo.grid.Grid} 
31765      */
31766     getGrid : function(){
31767         return this.grid;    
31768     },
31769     
31770     setSize : function(width, height){
31771         if(!this.ignoreResize(width, height)){
31772             var grid = this.grid;
31773             var size = this.adjustForComponents(width, height);
31774             grid.getGridEl().setSize(size.width, size.height);
31775             grid.autoSize();
31776         }
31777     },
31778     
31779     beforeSlide : function(){
31780         this.grid.getView().scroller.clip();
31781     },
31782     
31783     afterSlide : function(){
31784         this.grid.getView().scroller.unclip();
31785     },
31786     
31787     destroy : function(){
31788         this.grid.destroy();
31789         delete this.grid;
31790         Roo.GridPanel.superclass.destroy.call(this); 
31791     }
31792 });
31793
31794
31795 /**
31796  * @class Roo.NestedLayoutPanel
31797  * @extends Roo.ContentPanel
31798  * @constructor
31799  * Create a new NestedLayoutPanel.
31800  * 
31801  * 
31802  * @param {Roo.BorderLayout} layout [required] The layout for this panel
31803  * @param {String/Object} config A string to set only the title or a config object
31804  */
31805 Roo.NestedLayoutPanel = function(layout, config)
31806 {
31807     // construct with only one argument..
31808     /* FIXME - implement nicer consturctors
31809     if (layout.layout) {
31810         config = layout;
31811         layout = config.layout;
31812         delete config.layout;
31813     }
31814     if (layout.xtype && !layout.getEl) {
31815         // then layout needs constructing..
31816         layout = Roo.factory(layout, Roo);
31817     }
31818     */
31819     
31820     
31821     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31822     
31823     layout.monitorWindowResize = false; // turn off autosizing
31824     this.layout = layout;
31825     this.layout.getEl().addClass("x-layout-nested-layout");
31826     
31827     
31828     
31829     
31830 };
31831
31832 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31833
31834     setSize : function(width, height){
31835         if(!this.ignoreResize(width, height)){
31836             var size = this.adjustForComponents(width, height);
31837             var el = this.layout.getEl();
31838             el.setSize(size.width, size.height);
31839             var touch = el.dom.offsetWidth;
31840             this.layout.layout();
31841             // ie requires a double layout on the first pass
31842             if(Roo.isIE && !this.initialized){
31843                 this.initialized = true;
31844                 this.layout.layout();
31845             }
31846         }
31847     },
31848     
31849     // activate all subpanels if not currently active..
31850     
31851     setActiveState : function(active){
31852         this.active = active;
31853         if(!active){
31854             this.fireEvent("deactivate", this);
31855             return;
31856         }
31857         
31858         this.fireEvent("activate", this);
31859         // not sure if this should happen before or after..
31860         if (!this.layout) {
31861             return; // should not happen..
31862         }
31863         var reg = false;
31864         for (var r in this.layout.regions) {
31865             reg = this.layout.getRegion(r);
31866             if (reg.getActivePanel()) {
31867                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31868                 reg.setActivePanel(reg.getActivePanel());
31869                 continue;
31870             }
31871             if (!reg.panels.length) {
31872                 continue;
31873             }
31874             reg.showPanel(reg.getPanel(0));
31875         }
31876         
31877         
31878         
31879         
31880     },
31881     
31882     /**
31883      * Returns the nested BorderLayout for this panel
31884      * @return {Roo.BorderLayout} 
31885      */
31886     getLayout : function(){
31887         return this.layout;
31888     },
31889     
31890      /**
31891      * Adds a xtype elements to the layout of the nested panel
31892      * <pre><code>
31893
31894 panel.addxtype({
31895        xtype : 'ContentPanel',
31896        region: 'west',
31897        items: [ .... ]
31898    }
31899 );
31900
31901 panel.addxtype({
31902         xtype : 'NestedLayoutPanel',
31903         region: 'west',
31904         layout: {
31905            center: { },
31906            west: { }   
31907         },
31908         items : [ ... list of content panels or nested layout panels.. ]
31909    }
31910 );
31911 </code></pre>
31912      * @param {Object} cfg Xtype definition of item to add.
31913      */
31914     addxtype : function(cfg) {
31915         return this.layout.addxtype(cfg);
31916     
31917     }
31918 });
31919
31920 Roo.ScrollPanel = function(el, config, content){
31921     config = config || {};
31922     config.fitToFrame = true;
31923     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31924     
31925     this.el.dom.style.overflow = "hidden";
31926     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31927     this.el.removeClass("x-layout-inactive-content");
31928     this.el.on("mousewheel", this.onWheel, this);
31929
31930     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31931     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31932     up.unselectable(); down.unselectable();
31933     up.on("click", this.scrollUp, this);
31934     down.on("click", this.scrollDown, this);
31935     up.addClassOnOver("x-scroller-btn-over");
31936     down.addClassOnOver("x-scroller-btn-over");
31937     up.addClassOnClick("x-scroller-btn-click");
31938     down.addClassOnClick("x-scroller-btn-click");
31939     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31940
31941     this.resizeEl = this.el;
31942     this.el = wrap; this.up = up; this.down = down;
31943 };
31944
31945 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31946     increment : 100,
31947     wheelIncrement : 5,
31948     scrollUp : function(){
31949         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31950     },
31951
31952     scrollDown : function(){
31953         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31954     },
31955
31956     afterScroll : function(){
31957         var el = this.resizeEl;
31958         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31959         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31960         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31961     },
31962
31963     setSize : function(){
31964         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31965         this.afterScroll();
31966     },
31967
31968     onWheel : function(e){
31969         var d = e.getWheelDelta();
31970         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31971         this.afterScroll();
31972         e.stopEvent();
31973     },
31974
31975     setContent : function(content, loadScripts){
31976         this.resizeEl.update(content, loadScripts);
31977     }
31978
31979 });
31980
31981
31982
31983 /**
31984  * @class Roo.TreePanel
31985  * @extends Roo.ContentPanel
31986  * Treepanel component
31987  * 
31988  * @constructor
31989  * Create a new TreePanel. - defaults to fit/scoll contents.
31990  * @param {String/Object} config A string to set only the panel's title, or a config object
31991  */
31992 Roo.TreePanel = function(config){
31993     var el = config.el;
31994     var tree = config.tree;
31995     delete config.tree; 
31996     delete config.el; // hopefull!
31997     
31998     // wrapper for IE7 strict & safari scroll issue
31999     
32000     var treeEl = el.createChild();
32001     config.resizeEl = treeEl;
32002     
32003     
32004     
32005     Roo.TreePanel.superclass.constructor.call(this, el, config);
32006  
32007  
32008     this.tree = new Roo.tree.TreePanel(treeEl , tree);
32009     //console.log(tree);
32010     this.on('activate', function()
32011     {
32012         if (this.tree.rendered) {
32013             return;
32014         }
32015         //console.log('render tree');
32016         this.tree.render();
32017     });
32018     // this should not be needed.. - it's actually the 'el' that resizes?
32019     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
32020     
32021     //this.on('resize',  function (cp, w, h) {
32022     //        this.tree.innerCt.setWidth(w);
32023     //        this.tree.innerCt.setHeight(h);
32024     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
32025     //});
32026
32027         
32028     
32029 };
32030
32031 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
32032     fitToFrame : true,
32033     autoScroll : true,
32034     /*
32035      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
32036      */
32037     tree : false
32038
32039 });
32040
32041
32042
32043
32044
32045
32046
32047
32048
32049
32050
32051 /*
32052  * Based on:
32053  * Ext JS Library 1.1.1
32054  * Copyright(c) 2006-2007, Ext JS, LLC.
32055  *
32056  * Originally Released Under LGPL - original licence link has changed is not relivant.
32057  *
32058  * Fork - LGPL
32059  * <script type="text/javascript">
32060  */
32061  
32062
32063 /**
32064  * @class Roo.ReaderLayout
32065  * @extends Roo.BorderLayout
32066  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
32067  * center region containing two nested regions (a top one for a list view and one for item preview below),
32068  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
32069  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
32070  * expedites the setup of the overall layout and regions for this common application style.
32071  * Example:
32072  <pre><code>
32073 var reader = new Roo.ReaderLayout();
32074 var CP = Roo.ContentPanel;  // shortcut for adding
32075
32076 reader.beginUpdate();
32077 reader.add("north", new CP("north", "North"));
32078 reader.add("west", new CP("west", {title: "West"}));
32079 reader.add("east", new CP("east", {title: "East"}));
32080
32081 reader.regions.listView.add(new CP("listView", "List"));
32082 reader.regions.preview.add(new CP("preview", "Preview"));
32083 reader.endUpdate();
32084 </code></pre>
32085 * @constructor
32086 * Create a new ReaderLayout
32087 * @param {Object} config Configuration options
32088 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
32089 * document.body if omitted)
32090 */
32091 Roo.ReaderLayout = function(config, renderTo){
32092     var c = config || {size:{}};
32093     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
32094         north: c.north !== false ? Roo.apply({
32095             split:false,
32096             initialSize: 32,
32097             titlebar: false
32098         }, c.north) : false,
32099         west: c.west !== false ? Roo.apply({
32100             split:true,
32101             initialSize: 200,
32102             minSize: 175,
32103             maxSize: 400,
32104             titlebar: true,
32105             collapsible: true,
32106             animate: true,
32107             margins:{left:5,right:0,bottom:5,top:5},
32108             cmargins:{left:5,right:5,bottom:5,top:5}
32109         }, c.west) : false,
32110         east: c.east !== false ? Roo.apply({
32111             split:true,
32112             initialSize: 200,
32113             minSize: 175,
32114             maxSize: 400,
32115             titlebar: true,
32116             collapsible: true,
32117             animate: true,
32118             margins:{left:0,right:5,bottom:5,top:5},
32119             cmargins:{left:5,right:5,bottom:5,top:5}
32120         }, c.east) : false,
32121         center: Roo.apply({
32122             tabPosition: 'top',
32123             autoScroll:false,
32124             closeOnTab: true,
32125             titlebar:false,
32126             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32127         }, c.center)
32128     });
32129
32130     this.el.addClass('x-reader');
32131
32132     this.beginUpdate();
32133
32134     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32135         south: c.preview !== false ? Roo.apply({
32136             split:true,
32137             initialSize: 200,
32138             minSize: 100,
32139             autoScroll:true,
32140             collapsible:true,
32141             titlebar: true,
32142             cmargins:{top:5,left:0, right:0, bottom:0}
32143         }, c.preview) : false,
32144         center: Roo.apply({
32145             autoScroll:false,
32146             titlebar:false,
32147             minHeight:200
32148         }, c.listView)
32149     });
32150     this.add('center', new Roo.NestedLayoutPanel(inner,
32151             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
32152
32153     this.endUpdate();
32154
32155     this.regions.preview = inner.getRegion('south');
32156     this.regions.listView = inner.getRegion('center');
32157 };
32158
32159 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32160  * Based on:
32161  * Ext JS Library 1.1.1
32162  * Copyright(c) 2006-2007, Ext JS, LLC.
32163  *
32164  * Originally Released Under LGPL - original licence link has changed is not relivant.
32165  *
32166  * Fork - LGPL
32167  * <script type="text/javascript">
32168  */
32169  
32170 /**
32171  * @class Roo.grid.Grid
32172  * @extends Roo.util.Observable
32173  * This class represents the primary interface of a component based grid control.
32174  * <br><br>Usage:<pre><code>
32175  var grid = new Roo.grid.Grid("my-container-id", {
32176      ds: myDataStore,
32177      cm: myColModel,
32178      selModel: mySelectionModel,
32179      autoSizeColumns: true,
32180      monitorWindowResize: false,
32181      trackMouseOver: true
32182  });
32183  // set any options
32184  grid.render();
32185  * </code></pre>
32186  * <b>Common Problems:</b><br/>
32187  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32188  * element will correct this<br/>
32189  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32190  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32191  * are unpredictable.<br/>
32192  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32193  * grid to calculate dimensions/offsets.<br/>
32194   * @constructor
32195  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32196  * The container MUST have some type of size defined for the grid to fill. The container will be
32197  * automatically set to position relative if it isn't already.
32198  * @param {Object} config A config object that sets properties on this grid.
32199  */
32200 Roo.grid.Grid = function(container, config){
32201         // initialize the container
32202         this.container = Roo.get(container);
32203         this.container.update("");
32204         this.container.setStyle("overflow", "hidden");
32205     this.container.addClass('x-grid-container');
32206
32207     this.id = this.container.id;
32208
32209     Roo.apply(this, config);
32210     // check and correct shorthanded configs
32211     if(this.ds){
32212         this.dataSource = this.ds;
32213         delete this.ds;
32214     }
32215     if(this.cm){
32216         this.colModel = this.cm;
32217         delete this.cm;
32218     }
32219     if(this.sm){
32220         this.selModel = this.sm;
32221         delete this.sm;
32222     }
32223
32224     if (this.selModel) {
32225         this.selModel = Roo.factory(this.selModel, Roo.grid);
32226         this.sm = this.selModel;
32227         this.sm.xmodule = this.xmodule || false;
32228     }
32229     if (typeof(this.colModel.config) == 'undefined') {
32230         this.colModel = new Roo.grid.ColumnModel(this.colModel);
32231         this.cm = this.colModel;
32232         this.cm.xmodule = this.xmodule || false;
32233     }
32234     if (this.dataSource) {
32235         this.dataSource= Roo.factory(this.dataSource, Roo.data);
32236         this.ds = this.dataSource;
32237         this.ds.xmodule = this.xmodule || false;
32238          
32239     }
32240     
32241     
32242     
32243     if(this.width){
32244         this.container.setWidth(this.width);
32245     }
32246
32247     if(this.height){
32248         this.container.setHeight(this.height);
32249     }
32250     /** @private */
32251         this.addEvents({
32252         // raw events
32253         /**
32254          * @event click
32255          * The raw click event for the entire grid.
32256          * @param {Roo.EventObject} e
32257          */
32258         "click" : true,
32259         /**
32260          * @event dblclick
32261          * The raw dblclick event for the entire grid.
32262          * @param {Roo.EventObject} e
32263          */
32264         "dblclick" : true,
32265         /**
32266          * @event contextmenu
32267          * The raw contextmenu event for the entire grid.
32268          * @param {Roo.EventObject} e
32269          */
32270         "contextmenu" : true,
32271         /**
32272          * @event mousedown
32273          * The raw mousedown event for the entire grid.
32274          * @param {Roo.EventObject} e
32275          */
32276         "mousedown" : true,
32277         /**
32278          * @event mouseup
32279          * The raw mouseup event for the entire grid.
32280          * @param {Roo.EventObject} e
32281          */
32282         "mouseup" : true,
32283         /**
32284          * @event mouseover
32285          * The raw mouseover event for the entire grid.
32286          * @param {Roo.EventObject} e
32287          */
32288         "mouseover" : true,
32289         /**
32290          * @event mouseout
32291          * The raw mouseout event for the entire grid.
32292          * @param {Roo.EventObject} e
32293          */
32294         "mouseout" : true,
32295         /**
32296          * @event keypress
32297          * The raw keypress event for the entire grid.
32298          * @param {Roo.EventObject} e
32299          */
32300         "keypress" : true,
32301         /**
32302          * @event keydown
32303          * The raw keydown event for the entire grid.
32304          * @param {Roo.EventObject} e
32305          */
32306         "keydown" : true,
32307
32308         // custom events
32309
32310         /**
32311          * @event cellclick
32312          * Fires when a cell is clicked
32313          * @param {Grid} this
32314          * @param {Number} rowIndex
32315          * @param {Number} columnIndex
32316          * @param {Roo.EventObject} e
32317          */
32318         "cellclick" : true,
32319         /**
32320          * @event celldblclick
32321          * Fires when a cell is double clicked
32322          * @param {Grid} this
32323          * @param {Number} rowIndex
32324          * @param {Number} columnIndex
32325          * @param {Roo.EventObject} e
32326          */
32327         "celldblclick" : true,
32328         /**
32329          * @event rowclick
32330          * Fires when a row is clicked
32331          * @param {Grid} this
32332          * @param {Number} rowIndex
32333          * @param {Roo.EventObject} e
32334          */
32335         "rowclick" : true,
32336         /**
32337          * @event rowdblclick
32338          * Fires when a row is double clicked
32339          * @param {Grid} this
32340          * @param {Number} rowIndex
32341          * @param {Roo.EventObject} e
32342          */
32343         "rowdblclick" : true,
32344         /**
32345          * @event headerclick
32346          * Fires when a header is clicked
32347          * @param {Grid} this
32348          * @param {Number} columnIndex
32349          * @param {Roo.EventObject} e
32350          */
32351         "headerclick" : true,
32352         /**
32353          * @event headerdblclick
32354          * Fires when a header cell is double clicked
32355          * @param {Grid} this
32356          * @param {Number} columnIndex
32357          * @param {Roo.EventObject} e
32358          */
32359         "headerdblclick" : true,
32360         /**
32361          * @event rowcontextmenu
32362          * Fires when a row is right clicked
32363          * @param {Grid} this
32364          * @param {Number} rowIndex
32365          * @param {Roo.EventObject} e
32366          */
32367         "rowcontextmenu" : true,
32368         /**
32369          * @event cellcontextmenu
32370          * Fires when a cell is right clicked
32371          * @param {Grid} this
32372          * @param {Number} rowIndex
32373          * @param {Number} cellIndex
32374          * @param {Roo.EventObject} e
32375          */
32376          "cellcontextmenu" : true,
32377         /**
32378          * @event headercontextmenu
32379          * Fires when a header is right clicked
32380          * @param {Grid} this
32381          * @param {Number} columnIndex
32382          * @param {Roo.EventObject} e
32383          */
32384         "headercontextmenu" : true,
32385         /**
32386          * @event bodyscroll
32387          * Fires when the body element is scrolled
32388          * @param {Number} scrollLeft
32389          * @param {Number} scrollTop
32390          */
32391         "bodyscroll" : true,
32392         /**
32393          * @event columnresize
32394          * Fires when the user resizes a column
32395          * @param {Number} columnIndex
32396          * @param {Number} newSize
32397          */
32398         "columnresize" : true,
32399         /**
32400          * @event columnmove
32401          * Fires when the user moves a column
32402          * @param {Number} oldIndex
32403          * @param {Number} newIndex
32404          */
32405         "columnmove" : true,
32406         /**
32407          * @event startdrag
32408          * Fires when row(s) start being dragged
32409          * @param {Grid} this
32410          * @param {Roo.GridDD} dd The drag drop object
32411          * @param {event} e The raw browser event
32412          */
32413         "startdrag" : true,
32414         /**
32415          * @event enddrag
32416          * Fires when a drag operation is complete
32417          * @param {Grid} this
32418          * @param {Roo.GridDD} dd The drag drop object
32419          * @param {event} e The raw browser event
32420          */
32421         "enddrag" : true,
32422         /**
32423          * @event dragdrop
32424          * Fires when dragged row(s) are dropped on a valid DD target
32425          * @param {Grid} this
32426          * @param {Roo.GridDD} dd The drag drop object
32427          * @param {String} targetId The target drag drop object
32428          * @param {event} e The raw browser event
32429          */
32430         "dragdrop" : true,
32431         /**
32432          * @event dragover
32433          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32434          * @param {Grid} this
32435          * @param {Roo.GridDD} dd The drag drop object
32436          * @param {String} targetId The target drag drop object
32437          * @param {event} e The raw browser event
32438          */
32439         "dragover" : true,
32440         /**
32441          * @event dragenter
32442          *  Fires when the dragged row(s) first cross another DD target while being dragged
32443          * @param {Grid} this
32444          * @param {Roo.GridDD} dd The drag drop object
32445          * @param {String} targetId The target drag drop object
32446          * @param {event} e The raw browser event
32447          */
32448         "dragenter" : true,
32449         /**
32450          * @event dragout
32451          * Fires when the dragged row(s) leave another DD target while being dragged
32452          * @param {Grid} this
32453          * @param {Roo.GridDD} dd The drag drop object
32454          * @param {String} targetId The target drag drop object
32455          * @param {event} e The raw browser event
32456          */
32457         "dragout" : true,
32458         /**
32459          * @event rowclass
32460          * Fires when a row is rendered, so you can change add a style to it.
32461          * @param {GridView} gridview   The grid view
32462          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
32463          */
32464         'rowclass' : true,
32465
32466         /**
32467          * @event render
32468          * Fires when the grid is rendered
32469          * @param {Grid} grid
32470          */
32471         'render' : true
32472     });
32473
32474     Roo.grid.Grid.superclass.constructor.call(this);
32475 };
32476 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32477     
32478     /**
32479          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
32480          */
32481         /**
32482          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
32483          */
32484         /**
32485          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
32486          */
32487         /**
32488          * @cfg {Roo.grid.Store} ds The data store for the grid
32489          */
32490         /**
32491          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
32492          */
32493         /**
32494      * @cfg {String} ddGroup - drag drop group.
32495      */
32496       /**
32497      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
32498      */
32499
32500     /**
32501      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32502      */
32503     minColumnWidth : 25,
32504
32505     /**
32506      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32507      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32508      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32509      */
32510     autoSizeColumns : false,
32511
32512     /**
32513      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32514      */
32515     autoSizeHeaders : true,
32516
32517     /**
32518      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32519      */
32520     monitorWindowResize : true,
32521
32522     /**
32523      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32524      * rows measured to get a columns size. Default is 0 (all rows).
32525      */
32526     maxRowsToMeasure : 0,
32527
32528     /**
32529      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32530      */
32531     trackMouseOver : true,
32532
32533     /**
32534     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32535     */
32536       /**
32537     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
32538     */
32539     
32540     /**
32541     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32542     */
32543     enableDragDrop : false,
32544     
32545     /**
32546     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32547     */
32548     enableColumnMove : true,
32549     
32550     /**
32551     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32552     */
32553     enableColumnHide : true,
32554     
32555     /**
32556     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32557     */
32558     enableRowHeightSync : false,
32559     
32560     /**
32561     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32562     */
32563     stripeRows : true,
32564     
32565     /**
32566     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32567     */
32568     autoHeight : false,
32569
32570     /**
32571      * @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.
32572      */
32573     autoExpandColumn : false,
32574
32575     /**
32576     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32577     * Default is 50.
32578     */
32579     autoExpandMin : 50,
32580
32581     /**
32582     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32583     */
32584     autoExpandMax : 1000,
32585
32586     /**
32587     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32588     */
32589     view : null,
32590
32591     /**
32592     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32593     */
32594     loadMask : false,
32595     /**
32596     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32597     */
32598     dropTarget: false,
32599     
32600    
32601     
32602     // private
32603     rendered : false,
32604
32605     /**
32606     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32607     * of a fixed width. Default is false.
32608     */
32609     /**
32610     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32611     */
32612     
32613     
32614     /**
32615     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32616     * %0 is replaced with the number of selected rows.
32617     */
32618     ddText : "{0} selected row{1}",
32619     
32620     
32621     /**
32622      * Called once after all setup has been completed and the grid is ready to be rendered.
32623      * @return {Roo.grid.Grid} this
32624      */
32625     render : function()
32626     {
32627         var c = this.container;
32628         // try to detect autoHeight/width mode
32629         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32630             this.autoHeight = true;
32631         }
32632         var view = this.getView();
32633         view.init(this);
32634
32635         c.on("click", this.onClick, this);
32636         c.on("dblclick", this.onDblClick, this);
32637         c.on("contextmenu", this.onContextMenu, this);
32638         c.on("keydown", this.onKeyDown, this);
32639         if (Roo.isTouch) {
32640             c.on("touchstart", this.onTouchStart, this);
32641         }
32642
32643         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32644
32645         this.getSelectionModel().init(this);
32646
32647         view.render();
32648
32649         if(this.loadMask){
32650             this.loadMask = new Roo.LoadMask(this.container,
32651                     Roo.apply({store:this.dataSource}, this.loadMask));
32652         }
32653         
32654         
32655         if (this.toolbar && this.toolbar.xtype) {
32656             this.toolbar.container = this.getView().getHeaderPanel(true);
32657             this.toolbar = new Roo.Toolbar(this.toolbar);
32658         }
32659         if (this.footer && this.footer.xtype) {
32660             this.footer.dataSource = this.getDataSource();
32661             this.footer.container = this.getView().getFooterPanel(true);
32662             this.footer = Roo.factory(this.footer, Roo);
32663         }
32664         if (this.dropTarget && this.dropTarget.xtype) {
32665             delete this.dropTarget.xtype;
32666             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32667         }
32668         
32669         
32670         this.rendered = true;
32671         this.fireEvent('render', this);
32672         return this;
32673     },
32674
32675     /**
32676      * Reconfigures the grid to use a different Store and Column Model.
32677      * The View will be bound to the new objects and refreshed.
32678      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32679      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32680      */
32681     reconfigure : function(dataSource, colModel){
32682         if(this.loadMask){
32683             this.loadMask.destroy();
32684             this.loadMask = new Roo.LoadMask(this.container,
32685                     Roo.apply({store:dataSource}, this.loadMask));
32686         }
32687         this.view.bind(dataSource, colModel);
32688         this.dataSource = dataSource;
32689         this.colModel = colModel;
32690         this.view.refresh(true);
32691     },
32692     /**
32693      * addColumns
32694      * Add's a column, default at the end..
32695      
32696      * @param {int} position to add (default end)
32697      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
32698      */
32699     addColumns : function(pos, ar)
32700     {
32701         
32702         for (var i =0;i< ar.length;i++) {
32703             var cfg = ar[i];
32704             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
32705             this.cm.lookup[cfg.id] = cfg;
32706         }
32707         
32708         
32709         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
32710             pos = this.cm.config.length; //this.cm.config.push(cfg);
32711         } 
32712         pos = Math.max(0,pos);
32713         ar.unshift(0);
32714         ar.unshift(pos);
32715         this.cm.config.splice.apply(this.cm.config, ar);
32716         
32717         
32718         
32719         this.view.generateRules(this.cm);
32720         this.view.refresh(true);
32721         
32722     },
32723     
32724     
32725     
32726     
32727     // private
32728     onKeyDown : function(e){
32729         this.fireEvent("keydown", e);
32730     },
32731
32732     /**
32733      * Destroy this grid.
32734      * @param {Boolean} removeEl True to remove the element
32735      */
32736     destroy : function(removeEl, keepListeners){
32737         if(this.loadMask){
32738             this.loadMask.destroy();
32739         }
32740         var c = this.container;
32741         c.removeAllListeners();
32742         this.view.destroy();
32743         this.colModel.purgeListeners();
32744         if(!keepListeners){
32745             this.purgeListeners();
32746         }
32747         c.update("");
32748         if(removeEl === true){
32749             c.remove();
32750         }
32751     },
32752
32753     // private
32754     processEvent : function(name, e){
32755         // does this fire select???
32756         //Roo.log('grid:processEvent '  + name);
32757         
32758         if (name != 'touchstart' ) {
32759             this.fireEvent(name, e);    
32760         }
32761         
32762         var t = e.getTarget();
32763         var v = this.view;
32764         var header = v.findHeaderIndex(t);
32765         if(header !== false){
32766             var ename = name == 'touchstart' ? 'click' : name;
32767              
32768             this.fireEvent("header" + ename, this, header, e);
32769         }else{
32770             var row = v.findRowIndex(t);
32771             var cell = v.findCellIndex(t);
32772             if (name == 'touchstart') {
32773                 // first touch is always a click.
32774                 // hopefull this happens after selection is updated.?
32775                 name = false;
32776                 
32777                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32778                     var cs = this.selModel.getSelectedCell();
32779                     if (row == cs[0] && cell == cs[1]){
32780                         name = 'dblclick';
32781                     }
32782                 }
32783                 if (typeof(this.selModel.getSelections) != 'undefined') {
32784                     var cs = this.selModel.getSelections();
32785                     var ds = this.dataSource;
32786                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
32787                         name = 'dblclick';
32788                     }
32789                 }
32790                 if (!name) {
32791                     return;
32792                 }
32793             }
32794             
32795             
32796             if(row !== false){
32797                 this.fireEvent("row" + name, this, row, e);
32798                 if(cell !== false){
32799                     this.fireEvent("cell" + name, this, row, cell, e);
32800                 }
32801             }
32802         }
32803     },
32804
32805     // private
32806     onClick : function(e){
32807         this.processEvent("click", e);
32808     },
32809    // private
32810     onTouchStart : function(e){
32811         this.processEvent("touchstart", e);
32812     },
32813
32814     // private
32815     onContextMenu : function(e, t){
32816         this.processEvent("contextmenu", e);
32817     },
32818
32819     // private
32820     onDblClick : function(e){
32821         this.processEvent("dblclick", e);
32822     },
32823
32824     // private
32825     walkCells : function(row, col, step, fn, scope){
32826         var cm = this.colModel, clen = cm.getColumnCount();
32827         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32828         if(step < 0){
32829             if(col < 0){
32830                 row--;
32831                 first = false;
32832             }
32833             while(row >= 0){
32834                 if(!first){
32835                     col = clen-1;
32836                 }
32837                 first = false;
32838                 while(col >= 0){
32839                     if(fn.call(scope || this, row, col, cm) === true){
32840                         return [row, col];
32841                     }
32842                     col--;
32843                 }
32844                 row--;
32845             }
32846         } else {
32847             if(col >= clen){
32848                 row++;
32849                 first = false;
32850             }
32851             while(row < rlen){
32852                 if(!first){
32853                     col = 0;
32854                 }
32855                 first = false;
32856                 while(col < clen){
32857                     if(fn.call(scope || this, row, col, cm) === true){
32858                         return [row, col];
32859                     }
32860                     col++;
32861                 }
32862                 row++;
32863             }
32864         }
32865         return null;
32866     },
32867
32868     // private
32869     getSelections : function(){
32870         return this.selModel.getSelections();
32871     },
32872
32873     /**
32874      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32875      * but if manual update is required this method will initiate it.
32876      */
32877     autoSize : function(){
32878         if(this.rendered){
32879             this.view.layout();
32880             if(this.view.adjustForScroll){
32881                 this.view.adjustForScroll();
32882             }
32883         }
32884     },
32885
32886     /**
32887      * Returns the grid's underlying element.
32888      * @return {Element} The element
32889      */
32890     getGridEl : function(){
32891         return this.container;
32892     },
32893
32894     // private for compatibility, overridden by editor grid
32895     stopEditing : function(){},
32896
32897     /**
32898      * Returns the grid's SelectionModel.
32899      * @return {SelectionModel}
32900      */
32901     getSelectionModel : function(){
32902         if(!this.selModel){
32903             this.selModel = new Roo.grid.RowSelectionModel();
32904         }
32905         return this.selModel;
32906     },
32907
32908     /**
32909      * Returns the grid's DataSource.
32910      * @return {DataSource}
32911      */
32912     getDataSource : function(){
32913         return this.dataSource;
32914     },
32915
32916     /**
32917      * Returns the grid's ColumnModel.
32918      * @return {ColumnModel}
32919      */
32920     getColumnModel : function(){
32921         return this.colModel;
32922     },
32923
32924     /**
32925      * Returns the grid's GridView object.
32926      * @return {GridView}
32927      */
32928     getView : function(){
32929         if(!this.view){
32930             this.view = new Roo.grid.GridView(this.viewConfig);
32931             this.relayEvents(this.view, [
32932                 "beforerowremoved", "beforerowsinserted",
32933                 "beforerefresh", "rowremoved",
32934                 "rowsinserted", "rowupdated" ,"refresh"
32935             ]);
32936         }
32937         return this.view;
32938     },
32939     /**
32940      * Called to get grid's drag proxy text, by default returns this.ddText.
32941      * Override this to put something different in the dragged text.
32942      * @return {String}
32943      */
32944     getDragDropText : function(){
32945         var count = this.selModel.getCount();
32946         return String.format(this.ddText, count, count == 1 ? '' : 's');
32947     }
32948 });
32949 /*
32950  * Based on:
32951  * Ext JS Library 1.1.1
32952  * Copyright(c) 2006-2007, Ext JS, LLC.
32953  *
32954  * Originally Released Under LGPL - original licence link has changed is not relivant.
32955  *
32956  * Fork - LGPL
32957  * <script type="text/javascript">
32958  */
32959  /**
32960  * @class Roo.grid.AbstractGridView
32961  * @extends Roo.util.Observable
32962  * @abstract
32963  * Abstract base class for grid Views
32964  * @constructor
32965  */
32966 Roo.grid.AbstractGridView = function(){
32967         this.grid = null;
32968         
32969         this.events = {
32970             "beforerowremoved" : true,
32971             "beforerowsinserted" : true,
32972             "beforerefresh" : true,
32973             "rowremoved" : true,
32974             "rowsinserted" : true,
32975             "rowupdated" : true,
32976             "refresh" : true
32977         };
32978     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32979 };
32980
32981 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32982     rowClass : "x-grid-row",
32983     cellClass : "x-grid-cell",
32984     tdClass : "x-grid-td",
32985     hdClass : "x-grid-hd",
32986     splitClass : "x-grid-hd-split",
32987     
32988     init: function(grid){
32989         this.grid = grid;
32990                 var cid = this.grid.getGridEl().id;
32991         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32992         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32993         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32994         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32995         },
32996         
32997     getColumnRenderers : function(){
32998         var renderers = [];
32999         var cm = this.grid.colModel;
33000         var colCount = cm.getColumnCount();
33001         for(var i = 0; i < colCount; i++){
33002             renderers[i] = cm.getRenderer(i);
33003         }
33004         return renderers;
33005     },
33006     
33007     getColumnIds : function(){
33008         var ids = [];
33009         var cm = this.grid.colModel;
33010         var colCount = cm.getColumnCount();
33011         for(var i = 0; i < colCount; i++){
33012             ids[i] = cm.getColumnId(i);
33013         }
33014         return ids;
33015     },
33016     
33017     getDataIndexes : function(){
33018         if(!this.indexMap){
33019             this.indexMap = this.buildIndexMap();
33020         }
33021         return this.indexMap.colToData;
33022     },
33023     
33024     getColumnIndexByDataIndex : function(dataIndex){
33025         if(!this.indexMap){
33026             this.indexMap = this.buildIndexMap();
33027         }
33028         return this.indexMap.dataToCol[dataIndex];
33029     },
33030     
33031     /**
33032      * Set a css style for a column dynamically. 
33033      * @param {Number} colIndex The index of the column
33034      * @param {String} name The css property name
33035      * @param {String} value The css value
33036      */
33037     setCSSStyle : function(colIndex, name, value){
33038         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
33039         Roo.util.CSS.updateRule(selector, name, value);
33040     },
33041     
33042     generateRules : function(cm){
33043         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
33044         Roo.util.CSS.removeStyleSheet(rulesId);
33045         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33046             var cid = cm.getColumnId(i);
33047             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
33048                          this.tdSelector, cid, " {\n}\n",
33049                          this.hdSelector, cid, " {\n}\n",
33050                          this.splitSelector, cid, " {\n}\n");
33051         }
33052         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33053     }
33054 });/*
33055  * Based on:
33056  * Ext JS Library 1.1.1
33057  * Copyright(c) 2006-2007, Ext JS, LLC.
33058  *
33059  * Originally Released Under LGPL - original licence link has changed is not relivant.
33060  *
33061  * Fork - LGPL
33062  * <script type="text/javascript">
33063  */
33064
33065 // private
33066 // This is a support class used internally by the Grid components
33067 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
33068     this.grid = grid;
33069     this.view = grid.getView();
33070     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33071     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
33072     if(hd2){
33073         this.setHandleElId(Roo.id(hd));
33074         this.setOuterHandleElId(Roo.id(hd2));
33075     }
33076     this.scroll = false;
33077 };
33078 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
33079     maxDragWidth: 120,
33080     getDragData : function(e){
33081         var t = Roo.lib.Event.getTarget(e);
33082         var h = this.view.findHeaderCell(t);
33083         if(h){
33084             return {ddel: h.firstChild, header:h};
33085         }
33086         return false;
33087     },
33088
33089     onInitDrag : function(e){
33090         this.view.headersDisabled = true;
33091         var clone = this.dragData.ddel.cloneNode(true);
33092         clone.id = Roo.id();
33093         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
33094         this.proxy.update(clone);
33095         return true;
33096     },
33097
33098     afterValidDrop : function(){
33099         var v = this.view;
33100         setTimeout(function(){
33101             v.headersDisabled = false;
33102         }, 50);
33103     },
33104
33105     afterInvalidDrop : function(){
33106         var v = this.view;
33107         setTimeout(function(){
33108             v.headersDisabled = false;
33109         }, 50);
33110     }
33111 });
33112 /*
33113  * Based on:
33114  * Ext JS Library 1.1.1
33115  * Copyright(c) 2006-2007, Ext JS, LLC.
33116  *
33117  * Originally Released Under LGPL - original licence link has changed is not relivant.
33118  *
33119  * Fork - LGPL
33120  * <script type="text/javascript">
33121  */
33122 // private
33123 // This is a support class used internally by the Grid components
33124 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
33125     this.grid = grid;
33126     this.view = grid.getView();
33127     // split the proxies so they don't interfere with mouse events
33128     this.proxyTop = Roo.DomHelper.append(document.body, {
33129         cls:"col-move-top", html:"&#160;"
33130     }, true);
33131     this.proxyBottom = Roo.DomHelper.append(document.body, {
33132         cls:"col-move-bottom", html:"&#160;"
33133     }, true);
33134     this.proxyTop.hide = this.proxyBottom.hide = function(){
33135         this.setLeftTop(-100,-100);
33136         this.setStyle("visibility", "hidden");
33137     };
33138     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33139     // temporarily disabled
33140     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
33141     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
33142 };
33143 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
33144     proxyOffsets : [-4, -9],
33145     fly: Roo.Element.fly,
33146
33147     getTargetFromEvent : function(e){
33148         var t = Roo.lib.Event.getTarget(e);
33149         var cindex = this.view.findCellIndex(t);
33150         if(cindex !== false){
33151             return this.view.getHeaderCell(cindex);
33152         }
33153         return null;
33154     },
33155
33156     nextVisible : function(h){
33157         var v = this.view, cm = this.grid.colModel;
33158         h = h.nextSibling;
33159         while(h){
33160             if(!cm.isHidden(v.getCellIndex(h))){
33161                 return h;
33162             }
33163             h = h.nextSibling;
33164         }
33165         return null;
33166     },
33167
33168     prevVisible : function(h){
33169         var v = this.view, cm = this.grid.colModel;
33170         h = h.prevSibling;
33171         while(h){
33172             if(!cm.isHidden(v.getCellIndex(h))){
33173                 return h;
33174             }
33175             h = h.prevSibling;
33176         }
33177         return null;
33178     },
33179
33180     positionIndicator : function(h, n, e){
33181         var x = Roo.lib.Event.getPageX(e);
33182         var r = Roo.lib.Dom.getRegion(n.firstChild);
33183         var px, pt, py = r.top + this.proxyOffsets[1];
33184         if((r.right - x) <= (r.right-r.left)/2){
33185             px = r.right+this.view.borderWidth;
33186             pt = "after";
33187         }else{
33188             px = r.left;
33189             pt = "before";
33190         }
33191         var oldIndex = this.view.getCellIndex(h);
33192         var newIndex = this.view.getCellIndex(n);
33193
33194         if(this.grid.colModel.isFixed(newIndex)){
33195             return false;
33196         }
33197
33198         var locked = this.grid.colModel.isLocked(newIndex);
33199
33200         if(pt == "after"){
33201             newIndex++;
33202         }
33203         if(oldIndex < newIndex){
33204             newIndex--;
33205         }
33206         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33207             return false;
33208         }
33209         px +=  this.proxyOffsets[0];
33210         this.proxyTop.setLeftTop(px, py);
33211         this.proxyTop.show();
33212         if(!this.bottomOffset){
33213             this.bottomOffset = this.view.mainHd.getHeight();
33214         }
33215         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33216         this.proxyBottom.show();
33217         return pt;
33218     },
33219
33220     onNodeEnter : function(n, dd, e, data){
33221         if(data.header != n){
33222             this.positionIndicator(data.header, n, e);
33223         }
33224     },
33225
33226     onNodeOver : function(n, dd, e, data){
33227         var result = false;
33228         if(data.header != n){
33229             result = this.positionIndicator(data.header, n, e);
33230         }
33231         if(!result){
33232             this.proxyTop.hide();
33233             this.proxyBottom.hide();
33234         }
33235         return result ? this.dropAllowed : this.dropNotAllowed;
33236     },
33237
33238     onNodeOut : function(n, dd, e, data){
33239         this.proxyTop.hide();
33240         this.proxyBottom.hide();
33241     },
33242
33243     onNodeDrop : function(n, dd, e, data){
33244         var h = data.header;
33245         if(h != n){
33246             var cm = this.grid.colModel;
33247             var x = Roo.lib.Event.getPageX(e);
33248             var r = Roo.lib.Dom.getRegion(n.firstChild);
33249             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33250             var oldIndex = this.view.getCellIndex(h);
33251             var newIndex = this.view.getCellIndex(n);
33252             var locked = cm.isLocked(newIndex);
33253             if(pt == "after"){
33254                 newIndex++;
33255             }
33256             if(oldIndex < newIndex){
33257                 newIndex--;
33258             }
33259             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33260                 return false;
33261             }
33262             cm.setLocked(oldIndex, locked, true);
33263             cm.moveColumn(oldIndex, newIndex);
33264             this.grid.fireEvent("columnmove", oldIndex, newIndex);
33265             return true;
33266         }
33267         return false;
33268     }
33269 });
33270 /*
33271  * Based on:
33272  * Ext JS Library 1.1.1
33273  * Copyright(c) 2006-2007, Ext JS, LLC.
33274  *
33275  * Originally Released Under LGPL - original licence link has changed is not relivant.
33276  *
33277  * Fork - LGPL
33278  * <script type="text/javascript">
33279  */
33280   
33281 /**
33282  * @class Roo.grid.GridView
33283  * @extends Roo.util.Observable
33284  *
33285  * @constructor
33286  * @param {Object} config
33287  */
33288 Roo.grid.GridView = function(config){
33289     Roo.grid.GridView.superclass.constructor.call(this);
33290     this.el = null;
33291
33292     Roo.apply(this, config);
33293 };
33294
33295 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33296
33297     unselectable :  'unselectable="on"',
33298     unselectableCls :  'x-unselectable',
33299     
33300     
33301     rowClass : "x-grid-row",
33302
33303     cellClass : "x-grid-col",
33304
33305     tdClass : "x-grid-td",
33306
33307     hdClass : "x-grid-hd",
33308
33309     splitClass : "x-grid-split",
33310
33311     sortClasses : ["sort-asc", "sort-desc"],
33312
33313     enableMoveAnim : false,
33314
33315     hlColor: "C3DAF9",
33316
33317     dh : Roo.DomHelper,
33318
33319     fly : Roo.Element.fly,
33320
33321     css : Roo.util.CSS,
33322
33323     borderWidth: 1,
33324
33325     splitOffset: 3,
33326
33327     scrollIncrement : 22,
33328
33329     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33330
33331     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33332
33333     bind : function(ds, cm){
33334         if(this.ds){
33335             this.ds.un("load", this.onLoad, this);
33336             this.ds.un("datachanged", this.onDataChange, this);
33337             this.ds.un("add", this.onAdd, this);
33338             this.ds.un("remove", this.onRemove, this);
33339             this.ds.un("update", this.onUpdate, this);
33340             this.ds.un("clear", this.onClear, this);
33341         }
33342         if(ds){
33343             ds.on("load", this.onLoad, this);
33344             ds.on("datachanged", this.onDataChange, this);
33345             ds.on("add", this.onAdd, this);
33346             ds.on("remove", this.onRemove, this);
33347             ds.on("update", this.onUpdate, this);
33348             ds.on("clear", this.onClear, this);
33349         }
33350         this.ds = ds;
33351
33352         if(this.cm){
33353             this.cm.un("widthchange", this.onColWidthChange, this);
33354             this.cm.un("headerchange", this.onHeaderChange, this);
33355             this.cm.un("hiddenchange", this.onHiddenChange, this);
33356             this.cm.un("columnmoved", this.onColumnMove, this);
33357             this.cm.un("columnlockchange", this.onColumnLock, this);
33358         }
33359         if(cm){
33360             this.generateRules(cm);
33361             cm.on("widthchange", this.onColWidthChange, this);
33362             cm.on("headerchange", this.onHeaderChange, this);
33363             cm.on("hiddenchange", this.onHiddenChange, this);
33364             cm.on("columnmoved", this.onColumnMove, this);
33365             cm.on("columnlockchange", this.onColumnLock, this);
33366         }
33367         this.cm = cm;
33368     },
33369
33370     init: function(grid){
33371         Roo.grid.GridView.superclass.init.call(this, grid);
33372
33373         this.bind(grid.dataSource, grid.colModel);
33374
33375         grid.on("headerclick", this.handleHeaderClick, this);
33376
33377         if(grid.trackMouseOver){
33378             grid.on("mouseover", this.onRowOver, this);
33379             grid.on("mouseout", this.onRowOut, this);
33380         }
33381         grid.cancelTextSelection = function(){};
33382         this.gridId = grid.id;
33383
33384         var tpls = this.templates || {};
33385
33386         if(!tpls.master){
33387             tpls.master = new Roo.Template(
33388                '<div class="x-grid" hidefocus="true">',
33389                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
33390                   '<div class="x-grid-topbar"></div>',
33391                   '<div class="x-grid-scroller"><div></div></div>',
33392                   '<div class="x-grid-locked">',
33393                       '<div class="x-grid-header">{lockedHeader}</div>',
33394                       '<div class="x-grid-body">{lockedBody}</div>',
33395                   "</div>",
33396                   '<div class="x-grid-viewport">',
33397                       '<div class="x-grid-header">{header}</div>',
33398                       '<div class="x-grid-body">{body}</div>',
33399                   "</div>",
33400                   '<div class="x-grid-bottombar"></div>',
33401                  
33402                   '<div class="x-grid-resize-proxy">&#160;</div>',
33403                "</div>"
33404             );
33405             tpls.master.disableformats = true;
33406         }
33407
33408         if(!tpls.header){
33409             tpls.header = new Roo.Template(
33410                '<table border="0" cellspacing="0" cellpadding="0">',
33411                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
33412                "</table>{splits}"
33413             );
33414             tpls.header.disableformats = true;
33415         }
33416         tpls.header.compile();
33417
33418         if(!tpls.hcell){
33419             tpls.hcell = new Roo.Template(
33420                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
33421                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
33422                 "</div></td>"
33423              );
33424              tpls.hcell.disableFormats = true;
33425         }
33426         tpls.hcell.compile();
33427
33428         if(!tpls.hsplit){
33429             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
33430                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
33431             tpls.hsplit.disableFormats = true;
33432         }
33433         tpls.hsplit.compile();
33434
33435         if(!tpls.body){
33436             tpls.body = new Roo.Template(
33437                '<table border="0" cellspacing="0" cellpadding="0">',
33438                "<tbody>{rows}</tbody>",
33439                "</table>"
33440             );
33441             tpls.body.disableFormats = true;
33442         }
33443         tpls.body.compile();
33444
33445         if(!tpls.row){
33446             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
33447             tpls.row.disableFormats = true;
33448         }
33449         tpls.row.compile();
33450
33451         if(!tpls.cell){
33452             tpls.cell = new Roo.Template(
33453                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
33454                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
33455                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
33456                 "</td>"
33457             );
33458             tpls.cell.disableFormats = true;
33459         }
33460         tpls.cell.compile();
33461
33462         this.templates = tpls;
33463     },
33464
33465     // remap these for backwards compat
33466     onColWidthChange : function(){
33467         this.updateColumns.apply(this, arguments);
33468     },
33469     onHeaderChange : function(){
33470         this.updateHeaders.apply(this, arguments);
33471     }, 
33472     onHiddenChange : function(){
33473         this.handleHiddenChange.apply(this, arguments);
33474     },
33475     onColumnMove : function(){
33476         this.handleColumnMove.apply(this, arguments);
33477     },
33478     onColumnLock : function(){
33479         this.handleLockChange.apply(this, arguments);
33480     },
33481
33482     onDataChange : function(){
33483         this.refresh();
33484         this.updateHeaderSortState();
33485     },
33486
33487     onClear : function(){
33488         this.refresh();
33489     },
33490
33491     onUpdate : function(ds, record){
33492         this.refreshRow(record);
33493     },
33494
33495     refreshRow : function(record){
33496         var ds = this.ds, index;
33497         if(typeof record == 'number'){
33498             index = record;
33499             record = ds.getAt(index);
33500         }else{
33501             index = ds.indexOf(record);
33502         }
33503         this.insertRows(ds, index, index, true);
33504         this.onRemove(ds, record, index+1, true);
33505         this.syncRowHeights(index, index);
33506         this.layout();
33507         this.fireEvent("rowupdated", this, index, record);
33508     },
33509
33510     onAdd : function(ds, records, index){
33511         this.insertRows(ds, index, index + (records.length-1));
33512     },
33513
33514     onRemove : function(ds, record, index, isUpdate){
33515         if(isUpdate !== true){
33516             this.fireEvent("beforerowremoved", this, index, record);
33517         }
33518         var bt = this.getBodyTable(), lt = this.getLockedTable();
33519         if(bt.rows[index]){
33520             bt.firstChild.removeChild(bt.rows[index]);
33521         }
33522         if(lt.rows[index]){
33523             lt.firstChild.removeChild(lt.rows[index]);
33524         }
33525         if(isUpdate !== true){
33526             this.stripeRows(index);
33527             this.syncRowHeights(index, index);
33528             this.layout();
33529             this.fireEvent("rowremoved", this, index, record);
33530         }
33531     },
33532
33533     onLoad : function(){
33534         this.scrollToTop();
33535     },
33536
33537     /**
33538      * Scrolls the grid to the top
33539      */
33540     scrollToTop : function(){
33541         if(this.scroller){
33542             this.scroller.dom.scrollTop = 0;
33543             this.syncScroll();
33544         }
33545     },
33546
33547     /**
33548      * Gets a panel in the header of the grid that can be used for toolbars etc.
33549      * After modifying the contents of this panel a call to grid.autoSize() may be
33550      * required to register any changes in size.
33551      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33552      * @return Roo.Element
33553      */
33554     getHeaderPanel : function(doShow){
33555         if(doShow){
33556             this.headerPanel.show();
33557         }
33558         return this.headerPanel;
33559     },
33560
33561     /**
33562      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33563      * After modifying the contents of this panel a call to grid.autoSize() may be
33564      * required to register any changes in size.
33565      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33566      * @return Roo.Element
33567      */
33568     getFooterPanel : function(doShow){
33569         if(doShow){
33570             this.footerPanel.show();
33571         }
33572         return this.footerPanel;
33573     },
33574
33575     initElements : function(){
33576         var E = Roo.Element;
33577         var el = this.grid.getGridEl().dom.firstChild;
33578         var cs = el.childNodes;
33579
33580         this.el = new E(el);
33581         
33582          this.focusEl = new E(el.firstChild);
33583         this.focusEl.swallowEvent("click", true);
33584         
33585         this.headerPanel = new E(cs[1]);
33586         this.headerPanel.enableDisplayMode("block");
33587
33588         this.scroller = new E(cs[2]);
33589         this.scrollSizer = new E(this.scroller.dom.firstChild);
33590
33591         this.lockedWrap = new E(cs[3]);
33592         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33593         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33594
33595         this.mainWrap = new E(cs[4]);
33596         this.mainHd = new E(this.mainWrap.dom.firstChild);
33597         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33598
33599         this.footerPanel = new E(cs[5]);
33600         this.footerPanel.enableDisplayMode("block");
33601
33602         this.resizeProxy = new E(cs[6]);
33603
33604         this.headerSelector = String.format(
33605            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33606            this.lockedHd.id, this.mainHd.id
33607         );
33608
33609         this.splitterSelector = String.format(
33610            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33611            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33612         );
33613     },
33614     idToCssName : function(s)
33615     {
33616         return s.replace(/[^a-z0-9]+/ig, '-');
33617     },
33618
33619     getHeaderCell : function(index){
33620         return Roo.DomQuery.select(this.headerSelector)[index];
33621     },
33622
33623     getHeaderCellMeasure : function(index){
33624         return this.getHeaderCell(index).firstChild;
33625     },
33626
33627     getHeaderCellText : function(index){
33628         return this.getHeaderCell(index).firstChild.firstChild;
33629     },
33630
33631     getLockedTable : function(){
33632         return this.lockedBody.dom.firstChild;
33633     },
33634
33635     getBodyTable : function(){
33636         return this.mainBody.dom.firstChild;
33637     },
33638
33639     getLockedRow : function(index){
33640         return this.getLockedTable().rows[index];
33641     },
33642
33643     getRow : function(index){
33644         return this.getBodyTable().rows[index];
33645     },
33646
33647     getRowComposite : function(index){
33648         if(!this.rowEl){
33649             this.rowEl = new Roo.CompositeElementLite();
33650         }
33651         var els = [], lrow, mrow;
33652         if(lrow = this.getLockedRow(index)){
33653             els.push(lrow);
33654         }
33655         if(mrow = this.getRow(index)){
33656             els.push(mrow);
33657         }
33658         this.rowEl.elements = els;
33659         return this.rowEl;
33660     },
33661     /**
33662      * Gets the 'td' of the cell
33663      * 
33664      * @param {Integer} rowIndex row to select
33665      * @param {Integer} colIndex column to select
33666      * 
33667      * @return {Object} 
33668      */
33669     getCell : function(rowIndex, colIndex){
33670         var locked = this.cm.getLockedCount();
33671         var source;
33672         if(colIndex < locked){
33673             source = this.lockedBody.dom.firstChild;
33674         }else{
33675             source = this.mainBody.dom.firstChild;
33676             colIndex -= locked;
33677         }
33678         return source.rows[rowIndex].childNodes[colIndex];
33679     },
33680
33681     getCellText : function(rowIndex, colIndex){
33682         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33683     },
33684
33685     getCellBox : function(cell){
33686         var b = this.fly(cell).getBox();
33687         if(Roo.isOpera){ // opera fails to report the Y
33688             b.y = cell.offsetTop + this.mainBody.getY();
33689         }
33690         return b;
33691     },
33692
33693     getCellIndex : function(cell){
33694         var id = String(cell.className).match(this.cellRE);
33695         if(id){
33696             return parseInt(id[1], 10);
33697         }
33698         return 0;
33699     },
33700
33701     findHeaderIndex : function(n){
33702         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33703         return r ? this.getCellIndex(r) : false;
33704     },
33705
33706     findHeaderCell : function(n){
33707         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33708         return r ? r : false;
33709     },
33710
33711     findRowIndex : function(n){
33712         if(!n){
33713             return false;
33714         }
33715         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33716         return r ? r.rowIndex : false;
33717     },
33718
33719     findCellIndex : function(node){
33720         var stop = this.el.dom;
33721         while(node && node != stop){
33722             if(this.findRE.test(node.className)){
33723                 return this.getCellIndex(node);
33724             }
33725             node = node.parentNode;
33726         }
33727         return false;
33728     },
33729
33730     getColumnId : function(index){
33731         return this.cm.getColumnId(index);
33732     },
33733
33734     getSplitters : function()
33735     {
33736         if(this.splitterSelector){
33737            return Roo.DomQuery.select(this.splitterSelector);
33738         }else{
33739             return null;
33740       }
33741     },
33742
33743     getSplitter : function(index){
33744         return this.getSplitters()[index];
33745     },
33746
33747     onRowOver : function(e, t){
33748         var row;
33749         if((row = this.findRowIndex(t)) !== false){
33750             this.getRowComposite(row).addClass("x-grid-row-over");
33751         }
33752     },
33753
33754     onRowOut : function(e, t){
33755         var row;
33756         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33757             this.getRowComposite(row).removeClass("x-grid-row-over");
33758         }
33759     },
33760
33761     renderHeaders : function(){
33762         var cm = this.cm;
33763         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33764         var cb = [], lb = [], sb = [], lsb = [], p = {};
33765         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33766             p.cellId = "x-grid-hd-0-" + i;
33767             p.splitId = "x-grid-csplit-0-" + i;
33768             p.id = cm.getColumnId(i);
33769             p.value = cm.getColumnHeader(i) || "";
33770             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
33771             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33772             if(!cm.isLocked(i)){
33773                 cb[cb.length] = ct.apply(p);
33774                 sb[sb.length] = st.apply(p);
33775             }else{
33776                 lb[lb.length] = ct.apply(p);
33777                 lsb[lsb.length] = st.apply(p);
33778             }
33779         }
33780         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33781                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33782     },
33783
33784     updateHeaders : function(){
33785         var html = this.renderHeaders();
33786         this.lockedHd.update(html[0]);
33787         this.mainHd.update(html[1]);
33788     },
33789
33790     /**
33791      * Focuses the specified row.
33792      * @param {Number} row The row index
33793      */
33794     focusRow : function(row)
33795     {
33796         //Roo.log('GridView.focusRow');
33797         var x = this.scroller.dom.scrollLeft;
33798         this.focusCell(row, 0, false);
33799         this.scroller.dom.scrollLeft = x;
33800     },
33801
33802     /**
33803      * Focuses the specified cell.
33804      * @param {Number} row The row index
33805      * @param {Number} col The column index
33806      * @param {Boolean} hscroll false to disable horizontal scrolling
33807      */
33808     focusCell : function(row, col, hscroll)
33809     {
33810         //Roo.log('GridView.focusCell');
33811         var el = this.ensureVisible(row, col, hscroll);
33812         this.focusEl.alignTo(el, "tl-tl");
33813         if(Roo.isGecko){
33814             this.focusEl.focus();
33815         }else{
33816             this.focusEl.focus.defer(1, this.focusEl);
33817         }
33818     },
33819
33820     /**
33821      * Scrolls the specified cell into view
33822      * @param {Number} row The row index
33823      * @param {Number} col The column index
33824      * @param {Boolean} hscroll false to disable horizontal scrolling
33825      */
33826     ensureVisible : function(row, col, hscroll)
33827     {
33828         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33829         //return null; //disable for testing.
33830         if(typeof row != "number"){
33831             row = row.rowIndex;
33832         }
33833         if(row < 0 && row >= this.ds.getCount()){
33834             return  null;
33835         }
33836         col = (col !== undefined ? col : 0);
33837         var cm = this.grid.colModel;
33838         while(cm.isHidden(col)){
33839             col++;
33840         }
33841
33842         var el = this.getCell(row, col);
33843         if(!el){
33844             return null;
33845         }
33846         var c = this.scroller.dom;
33847
33848         var ctop = parseInt(el.offsetTop, 10);
33849         var cleft = parseInt(el.offsetLeft, 10);
33850         var cbot = ctop + el.offsetHeight;
33851         var cright = cleft + el.offsetWidth;
33852         
33853         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33854         var stop = parseInt(c.scrollTop, 10);
33855         var sleft = parseInt(c.scrollLeft, 10);
33856         var sbot = stop + ch;
33857         var sright = sleft + c.clientWidth;
33858         /*
33859         Roo.log('GridView.ensureVisible:' +
33860                 ' ctop:' + ctop +
33861                 ' c.clientHeight:' + c.clientHeight +
33862                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33863                 ' stop:' + stop +
33864                 ' cbot:' + cbot +
33865                 ' sbot:' + sbot +
33866                 ' ch:' + ch  
33867                 );
33868         */
33869         if(ctop < stop){
33870             c.scrollTop = ctop;
33871             //Roo.log("set scrolltop to ctop DISABLE?");
33872         }else if(cbot > sbot){
33873             //Roo.log("set scrolltop to cbot-ch");
33874             c.scrollTop = cbot-ch;
33875         }
33876         
33877         if(hscroll !== false){
33878             if(cleft < sleft){
33879                 c.scrollLeft = cleft;
33880             }else if(cright > sright){
33881                 c.scrollLeft = cright-c.clientWidth;
33882             }
33883         }
33884          
33885         return el;
33886     },
33887
33888     updateColumns : function(){
33889         this.grid.stopEditing();
33890         var cm = this.grid.colModel, colIds = this.getColumnIds();
33891         //var totalWidth = cm.getTotalWidth();
33892         var pos = 0;
33893         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33894             //if(cm.isHidden(i)) continue;
33895             var w = cm.getColumnWidth(i);
33896             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33897             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33898         }
33899         this.updateSplitters();
33900     },
33901
33902     generateRules : function(cm){
33903         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33904         Roo.util.CSS.removeStyleSheet(rulesId);
33905         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33906             var cid = cm.getColumnId(i);
33907             var align = '';
33908             if(cm.config[i].align){
33909                 align = 'text-align:'+cm.config[i].align+';';
33910             }
33911             var hidden = '';
33912             if(cm.isHidden(i)){
33913                 hidden = 'display:none;';
33914             }
33915             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33916             ruleBuf.push(
33917                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33918                     this.hdSelector, cid, " {\n", align, width, "}\n",
33919                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33920                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33921         }
33922         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33923     },
33924
33925     updateSplitters : function(){
33926         var cm = this.cm, s = this.getSplitters();
33927         if(s){ // splitters not created yet
33928             var pos = 0, locked = true;
33929             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33930                 if(cm.isHidden(i)) {
33931                     continue;
33932                 }
33933                 var w = cm.getColumnWidth(i); // make sure it's a number
33934                 if(!cm.isLocked(i) && locked){
33935                     pos = 0;
33936                     locked = false;
33937                 }
33938                 pos += w;
33939                 s[i].style.left = (pos-this.splitOffset) + "px";
33940             }
33941         }
33942     },
33943
33944     handleHiddenChange : function(colModel, colIndex, hidden){
33945         if(hidden){
33946             this.hideColumn(colIndex);
33947         }else{
33948             this.unhideColumn(colIndex);
33949         }
33950     },
33951
33952     hideColumn : function(colIndex){
33953         var cid = this.getColumnId(colIndex);
33954         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33955         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33956         if(Roo.isSafari){
33957             this.updateHeaders();
33958         }
33959         this.updateSplitters();
33960         this.layout();
33961     },
33962
33963     unhideColumn : function(colIndex){
33964         var cid = this.getColumnId(colIndex);
33965         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33966         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33967
33968         if(Roo.isSafari){
33969             this.updateHeaders();
33970         }
33971         this.updateSplitters();
33972         this.layout();
33973     },
33974
33975     insertRows : function(dm, firstRow, lastRow, isUpdate){
33976         if(firstRow == 0 && lastRow == dm.getCount()-1){
33977             this.refresh();
33978         }else{
33979             if(!isUpdate){
33980                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33981             }
33982             var s = this.getScrollState();
33983             var markup = this.renderRows(firstRow, lastRow);
33984             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33985             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33986             this.restoreScroll(s);
33987             if(!isUpdate){
33988                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33989                 this.syncRowHeights(firstRow, lastRow);
33990                 this.stripeRows(firstRow);
33991                 this.layout();
33992             }
33993         }
33994     },
33995
33996     bufferRows : function(markup, target, index){
33997         var before = null, trows = target.rows, tbody = target.tBodies[0];
33998         if(index < trows.length){
33999             before = trows[index];
34000         }
34001         var b = document.createElement("div");
34002         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
34003         var rows = b.firstChild.rows;
34004         for(var i = 0, len = rows.length; i < len; i++){
34005             if(before){
34006                 tbody.insertBefore(rows[0], before);
34007             }else{
34008                 tbody.appendChild(rows[0]);
34009             }
34010         }
34011         b.innerHTML = "";
34012         b = null;
34013     },
34014
34015     deleteRows : function(dm, firstRow, lastRow){
34016         if(dm.getRowCount()<1){
34017             this.fireEvent("beforerefresh", this);
34018             this.mainBody.update("");
34019             this.lockedBody.update("");
34020             this.fireEvent("refresh", this);
34021         }else{
34022             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
34023             var bt = this.getBodyTable();
34024             var tbody = bt.firstChild;
34025             var rows = bt.rows;
34026             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
34027                 tbody.removeChild(rows[firstRow]);
34028             }
34029             this.stripeRows(firstRow);
34030             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
34031         }
34032     },
34033
34034     updateRows : function(dataSource, firstRow, lastRow){
34035         var s = this.getScrollState();
34036         this.refresh();
34037         this.restoreScroll(s);
34038     },
34039
34040     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
34041         if(!noRefresh){
34042            this.refresh();
34043         }
34044         this.updateHeaderSortState();
34045     },
34046
34047     getScrollState : function(){
34048         
34049         var sb = this.scroller.dom;
34050         return {left: sb.scrollLeft, top: sb.scrollTop};
34051     },
34052
34053     stripeRows : function(startRow){
34054         if(!this.grid.stripeRows || this.ds.getCount() < 1){
34055             return;
34056         }
34057         startRow = startRow || 0;
34058         var rows = this.getBodyTable().rows;
34059         var lrows = this.getLockedTable().rows;
34060         var cls = ' x-grid-row-alt ';
34061         for(var i = startRow, len = rows.length; i < len; i++){
34062             var row = rows[i], lrow = lrows[i];
34063             var isAlt = ((i+1) % 2 == 0);
34064             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
34065             if(isAlt == hasAlt){
34066                 continue;
34067             }
34068             if(isAlt){
34069                 row.className += " x-grid-row-alt";
34070             }else{
34071                 row.className = row.className.replace("x-grid-row-alt", "");
34072             }
34073             if(lrow){
34074                 lrow.className = row.className;
34075             }
34076         }
34077     },
34078
34079     restoreScroll : function(state){
34080         //Roo.log('GridView.restoreScroll');
34081         var sb = this.scroller.dom;
34082         sb.scrollLeft = state.left;
34083         sb.scrollTop = state.top;
34084         this.syncScroll();
34085     },
34086
34087     syncScroll : function(){
34088         //Roo.log('GridView.syncScroll');
34089         var sb = this.scroller.dom;
34090         var sh = this.mainHd.dom;
34091         var bs = this.mainBody.dom;
34092         var lv = this.lockedBody.dom;
34093         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
34094         lv.scrollTop = bs.scrollTop = sb.scrollTop;
34095     },
34096
34097     handleScroll : function(e){
34098         this.syncScroll();
34099         var sb = this.scroller.dom;
34100         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
34101         e.stopEvent();
34102     },
34103
34104     handleWheel : function(e){
34105         var d = e.getWheelDelta();
34106         this.scroller.dom.scrollTop -= d*22;
34107         // set this here to prevent jumpy scrolling on large tables
34108         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
34109         e.stopEvent();
34110     },
34111
34112     renderRows : function(startRow, endRow){
34113         // pull in all the crap needed to render rows
34114         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
34115         var colCount = cm.getColumnCount();
34116
34117         if(ds.getCount() < 1){
34118             return ["", ""];
34119         }
34120
34121         // build a map for all the columns
34122         var cs = [];
34123         for(var i = 0; i < colCount; i++){
34124             var name = cm.getDataIndex(i);
34125             cs[i] = {
34126                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
34127                 renderer : cm.getRenderer(i),
34128                 id : cm.getColumnId(i),
34129                 locked : cm.isLocked(i),
34130                 has_editor : cm.isCellEditable(i)
34131             };
34132         }
34133
34134         startRow = startRow || 0;
34135         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
34136
34137         // records to render
34138         var rs = ds.getRange(startRow, endRow);
34139
34140         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
34141     },
34142
34143     // As much as I hate to duplicate code, this was branched because FireFox really hates
34144     // [].join("") on strings. The performance difference was substantial enough to
34145     // branch this function
34146     doRender : Roo.isGecko ?
34147             function(cs, rs, ds, startRow, colCount, stripe){
34148                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34149                 // buffers
34150                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34151                 
34152                 var hasListener = this.grid.hasListener('rowclass');
34153                 var rowcfg = {};
34154                 for(var j = 0, len = rs.length; j < len; j++){
34155                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34156                     for(var i = 0; i < colCount; i++){
34157                         c = cs[i];
34158                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34159                         p.id = c.id;
34160                         p.css = p.attr = "";
34161                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34162                         if(p.value == undefined || p.value === "") {
34163                             p.value = "&#160;";
34164                         }
34165                         if(c.has_editor){
34166                             p.css += ' x-grid-editable-cell';
34167                         }
34168                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
34169                             p.css +=  ' x-grid-dirty-cell';
34170                         }
34171                         var markup = ct.apply(p);
34172                         if(!c.locked){
34173                             cb+= markup;
34174                         }else{
34175                             lcb+= markup;
34176                         }
34177                     }
34178                     var alt = [];
34179                     if(stripe && ((rowIndex+1) % 2 == 0)){
34180                         alt.push("x-grid-row-alt")
34181                     }
34182                     if(r.dirty){
34183                         alt.push(  " x-grid-dirty-row");
34184                     }
34185                     rp.cells = lcb;
34186                     if(this.getRowClass){
34187                         alt.push(this.getRowClass(r, rowIndex));
34188                     }
34189                     if (hasListener) {
34190                         rowcfg = {
34191                              
34192                             record: r,
34193                             rowIndex : rowIndex,
34194                             rowClass : ''
34195                         };
34196                         this.grid.fireEvent('rowclass', this, rowcfg);
34197                         alt.push(rowcfg.rowClass);
34198                     }
34199                     rp.alt = alt.join(" ");
34200                     lbuf+= rt.apply(rp);
34201                     rp.cells = cb;
34202                     buf+=  rt.apply(rp);
34203                 }
34204                 return [lbuf, buf];
34205             } :
34206             function(cs, rs, ds, startRow, colCount, stripe){
34207                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34208                 // buffers
34209                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34210                 var hasListener = this.grid.hasListener('rowclass');
34211  
34212                 var rowcfg = {};
34213                 for(var j = 0, len = rs.length; j < len; j++){
34214                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
34215                     for(var i = 0; i < colCount; i++){
34216                         c = cs[i];
34217                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34218                         p.id = c.id;
34219                         p.css = p.attr = "";
34220                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34221                         if(p.value == undefined || p.value === "") {
34222                             p.value = "&#160;";
34223                         }
34224                         //Roo.log(c);
34225                          if(c.has_editor){
34226                             p.css += ' x-grid-editable-cell';
34227                         }
34228                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34229                             p.css += ' x-grid-dirty-cell' 
34230                         }
34231                         
34232                         var markup = ct.apply(p);
34233                         if(!c.locked){
34234                             cb[cb.length] = markup;
34235                         }else{
34236                             lcb[lcb.length] = markup;
34237                         }
34238                     }
34239                     var alt = [];
34240                     if(stripe && ((rowIndex+1) % 2 == 0)){
34241                         alt.push( "x-grid-row-alt");
34242                     }
34243                     if(r.dirty){
34244                         alt.push(" x-grid-dirty-row");
34245                     }
34246                     rp.cells = lcb;
34247                     if(this.getRowClass){
34248                         alt.push( this.getRowClass(r, rowIndex));
34249                     }
34250                     if (hasListener) {
34251                         rowcfg = {
34252                              
34253                             record: r,
34254                             rowIndex : rowIndex,
34255                             rowClass : ''
34256                         };
34257                         this.grid.fireEvent('rowclass', this, rowcfg);
34258                         alt.push(rowcfg.rowClass);
34259                     }
34260                     
34261                     rp.alt = alt.join(" ");
34262                     rp.cells = lcb.join("");
34263                     lbuf[lbuf.length] = rt.apply(rp);
34264                     rp.cells = cb.join("");
34265                     buf[buf.length] =  rt.apply(rp);
34266                 }
34267                 return [lbuf.join(""), buf.join("")];
34268             },
34269
34270     renderBody : function(){
34271         var markup = this.renderRows();
34272         var bt = this.templates.body;
34273         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34274     },
34275
34276     /**
34277      * Refreshes the grid
34278      * @param {Boolean} headersToo
34279      */
34280     refresh : function(headersToo){
34281         this.fireEvent("beforerefresh", this);
34282         this.grid.stopEditing();
34283         var result = this.renderBody();
34284         this.lockedBody.update(result[0]);
34285         this.mainBody.update(result[1]);
34286         if(headersToo === true){
34287             this.updateHeaders();
34288             this.updateColumns();
34289             this.updateSplitters();
34290             this.updateHeaderSortState();
34291         }
34292         this.syncRowHeights();
34293         this.layout();
34294         this.fireEvent("refresh", this);
34295     },
34296
34297     handleColumnMove : function(cm, oldIndex, newIndex){
34298         this.indexMap = null;
34299         var s = this.getScrollState();
34300         this.refresh(true);
34301         this.restoreScroll(s);
34302         this.afterMove(newIndex);
34303     },
34304
34305     afterMove : function(colIndex){
34306         if(this.enableMoveAnim && Roo.enableFx){
34307             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34308         }
34309         // if multisort - fix sortOrder, and reload..
34310         if (this.grid.dataSource.multiSort) {
34311             // the we can call sort again..
34312             var dm = this.grid.dataSource;
34313             var cm = this.grid.colModel;
34314             var so = [];
34315             for(var i = 0; i < cm.config.length; i++ ) {
34316                 
34317                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34318                     continue; // dont' bother, it's not in sort list or being set.
34319                 }
34320                 
34321                 so.push(cm.config[i].dataIndex);
34322             };
34323             dm.sortOrder = so;
34324             dm.load(dm.lastOptions);
34325             
34326             
34327         }
34328         
34329     },
34330
34331     updateCell : function(dm, rowIndex, dataIndex){
34332         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34333         if(typeof colIndex == "undefined"){ // not present in grid
34334             return;
34335         }
34336         var cm = this.grid.colModel;
34337         var cell = this.getCell(rowIndex, colIndex);
34338         var cellText = this.getCellText(rowIndex, colIndex);
34339
34340         var p = {
34341             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34342             id : cm.getColumnId(colIndex),
34343             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34344         };
34345         var renderer = cm.getRenderer(colIndex);
34346         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34347         if(typeof val == "undefined" || val === "") {
34348             val = "&#160;";
34349         }
34350         cellText.innerHTML = val;
34351         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34352         this.syncRowHeights(rowIndex, rowIndex);
34353     },
34354
34355     calcColumnWidth : function(colIndex, maxRowsToMeasure){
34356         var maxWidth = 0;
34357         if(this.grid.autoSizeHeaders){
34358             var h = this.getHeaderCellMeasure(colIndex);
34359             maxWidth = Math.max(maxWidth, h.scrollWidth);
34360         }
34361         var tb, index;
34362         if(this.cm.isLocked(colIndex)){
34363             tb = this.getLockedTable();
34364             index = colIndex;
34365         }else{
34366             tb = this.getBodyTable();
34367             index = colIndex - this.cm.getLockedCount();
34368         }
34369         if(tb && tb.rows){
34370             var rows = tb.rows;
34371             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34372             for(var i = 0; i < stopIndex; i++){
34373                 var cell = rows[i].childNodes[index].firstChild;
34374                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34375             }
34376         }
34377         return maxWidth + /*margin for error in IE*/ 5;
34378     },
34379     /**
34380      * Autofit a column to its content.
34381      * @param {Number} colIndex
34382      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34383      */
34384      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34385          if(this.cm.isHidden(colIndex)){
34386              return; // can't calc a hidden column
34387          }
34388         if(forceMinSize){
34389             var cid = this.cm.getColumnId(colIndex);
34390             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34391            if(this.grid.autoSizeHeaders){
34392                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34393            }
34394         }
34395         var newWidth = this.calcColumnWidth(colIndex);
34396         this.cm.setColumnWidth(colIndex,
34397             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34398         if(!suppressEvent){
34399             this.grid.fireEvent("columnresize", colIndex, newWidth);
34400         }
34401     },
34402
34403     /**
34404      * Autofits all columns to their content and then expands to fit any extra space in the grid
34405      */
34406      autoSizeColumns : function(){
34407         var cm = this.grid.colModel;
34408         var colCount = cm.getColumnCount();
34409         for(var i = 0; i < colCount; i++){
34410             this.autoSizeColumn(i, true, true);
34411         }
34412         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
34413             this.fitColumns();
34414         }else{
34415             this.updateColumns();
34416             this.layout();
34417         }
34418     },
34419
34420     /**
34421      * Autofits all columns to the grid's width proportionate with their current size
34422      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
34423      */
34424     fitColumns : function(reserveScrollSpace){
34425         var cm = this.grid.colModel;
34426         var colCount = cm.getColumnCount();
34427         var cols = [];
34428         var width = 0;
34429         var i, w;
34430         for (i = 0; i < colCount; i++){
34431             if(!cm.isHidden(i) && !cm.isFixed(i)){
34432                 w = cm.getColumnWidth(i);
34433                 cols.push(i);
34434                 cols.push(w);
34435                 width += w;
34436             }
34437         }
34438         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
34439         if(reserveScrollSpace){
34440             avail -= 17;
34441         }
34442         var frac = (avail - cm.getTotalWidth())/width;
34443         while (cols.length){
34444             w = cols.pop();
34445             i = cols.pop();
34446             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
34447         }
34448         this.updateColumns();
34449         this.layout();
34450     },
34451
34452     onRowSelect : function(rowIndex){
34453         var row = this.getRowComposite(rowIndex);
34454         row.addClass("x-grid-row-selected");
34455     },
34456
34457     onRowDeselect : function(rowIndex){
34458         var row = this.getRowComposite(rowIndex);
34459         row.removeClass("x-grid-row-selected");
34460     },
34461
34462     onCellSelect : function(row, col){
34463         var cell = this.getCell(row, col);
34464         if(cell){
34465             Roo.fly(cell).addClass("x-grid-cell-selected");
34466         }
34467     },
34468
34469     onCellDeselect : function(row, col){
34470         var cell = this.getCell(row, col);
34471         if(cell){
34472             Roo.fly(cell).removeClass("x-grid-cell-selected");
34473         }
34474     },
34475
34476     updateHeaderSortState : function(){
34477         
34478         // sort state can be single { field: xxx, direction : yyy}
34479         // or   { xxx=>ASC , yyy : DESC ..... }
34480         
34481         var mstate = {};
34482         if (!this.ds.multiSort) { 
34483             var state = this.ds.getSortState();
34484             if(!state){
34485                 return;
34486             }
34487             mstate[state.field] = state.direction;
34488             // FIXME... - this is not used here.. but might be elsewhere..
34489             this.sortState = state;
34490             
34491         } else {
34492             mstate = this.ds.sortToggle;
34493         }
34494         //remove existing sort classes..
34495         
34496         var sc = this.sortClasses;
34497         var hds = this.el.select(this.headerSelector).removeClass(sc);
34498         
34499         for(var f in mstate) {
34500         
34501             var sortColumn = this.cm.findColumnIndex(f);
34502             
34503             if(sortColumn != -1){
34504                 var sortDir = mstate[f];        
34505                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34506             }
34507         }
34508         
34509          
34510         
34511     },
34512
34513
34514     handleHeaderClick : function(g, index,e){
34515         
34516         Roo.log("header click");
34517         
34518         if (Roo.isTouch) {
34519             // touch events on header are handled by context
34520             this.handleHdCtx(g,index,e);
34521             return;
34522         }
34523         
34524         
34525         if(this.headersDisabled){
34526             return;
34527         }
34528         var dm = g.dataSource, cm = g.colModel;
34529         if(!cm.isSortable(index)){
34530             return;
34531         }
34532         g.stopEditing();
34533         
34534         if (dm.multiSort) {
34535             // update the sortOrder
34536             var so = [];
34537             for(var i = 0; i < cm.config.length; i++ ) {
34538                 
34539                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34540                     continue; // dont' bother, it's not in sort list or being set.
34541                 }
34542                 
34543                 so.push(cm.config[i].dataIndex);
34544             };
34545             dm.sortOrder = so;
34546         }
34547         
34548         
34549         dm.sort(cm.getDataIndex(index));
34550     },
34551
34552
34553     destroy : function(){
34554         if(this.colMenu){
34555             this.colMenu.removeAll();
34556             Roo.menu.MenuMgr.unregister(this.colMenu);
34557             this.colMenu.getEl().remove();
34558             delete this.colMenu;
34559         }
34560         if(this.hmenu){
34561             this.hmenu.removeAll();
34562             Roo.menu.MenuMgr.unregister(this.hmenu);
34563             this.hmenu.getEl().remove();
34564             delete this.hmenu;
34565         }
34566         if(this.grid.enableColumnMove){
34567             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34568             if(dds){
34569                 for(var dd in dds){
34570                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34571                         var elid = dds[dd].dragElId;
34572                         dds[dd].unreg();
34573                         Roo.get(elid).remove();
34574                     } else if(dds[dd].config.isTarget){
34575                         dds[dd].proxyTop.remove();
34576                         dds[dd].proxyBottom.remove();
34577                         dds[dd].unreg();
34578                     }
34579                     if(Roo.dd.DDM.locationCache[dd]){
34580                         delete Roo.dd.DDM.locationCache[dd];
34581                     }
34582                 }
34583                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34584             }
34585         }
34586         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34587         this.bind(null, null);
34588         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34589     },
34590
34591     handleLockChange : function(){
34592         this.refresh(true);
34593     },
34594
34595     onDenyColumnLock : function(){
34596
34597     },
34598
34599     onDenyColumnHide : function(){
34600
34601     },
34602
34603     handleHdMenuClick : function(item){
34604         var index = this.hdCtxIndex;
34605         var cm = this.cm, ds = this.ds;
34606         switch(item.id){
34607             case "asc":
34608                 ds.sort(cm.getDataIndex(index), "ASC");
34609                 break;
34610             case "desc":
34611                 ds.sort(cm.getDataIndex(index), "DESC");
34612                 break;
34613             case "lock":
34614                 var lc = cm.getLockedCount();
34615                 if(cm.getColumnCount(true) <= lc+1){
34616                     this.onDenyColumnLock();
34617                     return;
34618                 }
34619                 if(lc != index){
34620                     cm.setLocked(index, true, true);
34621                     cm.moveColumn(index, lc);
34622                     this.grid.fireEvent("columnmove", index, lc);
34623                 }else{
34624                     cm.setLocked(index, true);
34625                 }
34626             break;
34627             case "unlock":
34628                 var lc = cm.getLockedCount();
34629                 if((lc-1) != index){
34630                     cm.setLocked(index, false, true);
34631                     cm.moveColumn(index, lc-1);
34632                     this.grid.fireEvent("columnmove", index, lc-1);
34633                 }else{
34634                     cm.setLocked(index, false);
34635                 }
34636             break;
34637             case 'wider': // used to expand cols on touch..
34638             case 'narrow':
34639                 var cw = cm.getColumnWidth(index);
34640                 cw += (item.id == 'wider' ? 1 : -1) * 50;
34641                 cw = Math.max(0, cw);
34642                 cw = Math.min(cw,4000);
34643                 cm.setColumnWidth(index, cw);
34644                 break;
34645                 
34646             default:
34647                 index = cm.getIndexById(item.id.substr(4));
34648                 if(index != -1){
34649                     if(item.checked && cm.getColumnCount(true) <= 1){
34650                         this.onDenyColumnHide();
34651                         return false;
34652                     }
34653                     cm.setHidden(index, item.checked);
34654                 }
34655         }
34656         return true;
34657     },
34658
34659     beforeColMenuShow : function(){
34660         var cm = this.cm,  colCount = cm.getColumnCount();
34661         this.colMenu.removeAll();
34662         for(var i = 0; i < colCount; i++){
34663             this.colMenu.add(new Roo.menu.CheckItem({
34664                 id: "col-"+cm.getColumnId(i),
34665                 text: cm.getColumnHeader(i),
34666                 checked: !cm.isHidden(i),
34667                 hideOnClick:false
34668             }));
34669         }
34670     },
34671
34672     handleHdCtx : function(g, index, e){
34673         e.stopEvent();
34674         var hd = this.getHeaderCell(index);
34675         this.hdCtxIndex = index;
34676         var ms = this.hmenu.items, cm = this.cm;
34677         ms.get("asc").setDisabled(!cm.isSortable(index));
34678         ms.get("desc").setDisabled(!cm.isSortable(index));
34679         if(this.grid.enableColLock !== false){
34680             ms.get("lock").setDisabled(cm.isLocked(index));
34681             ms.get("unlock").setDisabled(!cm.isLocked(index));
34682         }
34683         this.hmenu.show(hd, "tl-bl");
34684     },
34685
34686     handleHdOver : function(e){
34687         var hd = this.findHeaderCell(e.getTarget());
34688         if(hd && !this.headersDisabled){
34689             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34690                this.fly(hd).addClass("x-grid-hd-over");
34691             }
34692         }
34693     },
34694
34695     handleHdOut : function(e){
34696         var hd = this.findHeaderCell(e.getTarget());
34697         if(hd){
34698             this.fly(hd).removeClass("x-grid-hd-over");
34699         }
34700     },
34701
34702     handleSplitDblClick : function(e, t){
34703         var i = this.getCellIndex(t);
34704         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34705             this.autoSizeColumn(i, true);
34706             this.layout();
34707         }
34708     },
34709
34710     render : function(){
34711
34712         var cm = this.cm;
34713         var colCount = cm.getColumnCount();
34714
34715         if(this.grid.monitorWindowResize === true){
34716             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34717         }
34718         var header = this.renderHeaders();
34719         var body = this.templates.body.apply({rows:""});
34720         var html = this.templates.master.apply({
34721             lockedBody: body,
34722             body: body,
34723             lockedHeader: header[0],
34724             header: header[1]
34725         });
34726
34727         //this.updateColumns();
34728
34729         this.grid.getGridEl().dom.innerHTML = html;
34730
34731         this.initElements();
34732         
34733         // a kludge to fix the random scolling effect in webkit
34734         this.el.on("scroll", function() {
34735             this.el.dom.scrollTop=0; // hopefully not recursive..
34736         },this);
34737
34738         this.scroller.on("scroll", this.handleScroll, this);
34739         this.lockedBody.on("mousewheel", this.handleWheel, this);
34740         this.mainBody.on("mousewheel", this.handleWheel, this);
34741
34742         this.mainHd.on("mouseover", this.handleHdOver, this);
34743         this.mainHd.on("mouseout", this.handleHdOut, this);
34744         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34745                 {delegate: "."+this.splitClass});
34746
34747         this.lockedHd.on("mouseover", this.handleHdOver, this);
34748         this.lockedHd.on("mouseout", this.handleHdOut, this);
34749         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34750                 {delegate: "."+this.splitClass});
34751
34752         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34753             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34754         }
34755
34756         this.updateSplitters();
34757
34758         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34759             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34760             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34761         }
34762
34763         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34764             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34765             this.hmenu.add(
34766                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34767                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34768             );
34769             if(this.grid.enableColLock !== false){
34770                 this.hmenu.add('-',
34771                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34772                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34773                 );
34774             }
34775             if (Roo.isTouch) {
34776                  this.hmenu.add('-',
34777                     {id:"wider", text: this.columnsWiderText},
34778                     {id:"narrow", text: this.columnsNarrowText }
34779                 );
34780                 
34781                  
34782             }
34783             
34784             if(this.grid.enableColumnHide !== false){
34785
34786                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34787                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34788                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34789
34790                 this.hmenu.add('-',
34791                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34792                 );
34793             }
34794             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34795
34796             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34797         }
34798
34799         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34800             this.dd = new Roo.grid.GridDragZone(this.grid, {
34801                 ddGroup : this.grid.ddGroup || 'GridDD'
34802             });
34803             
34804         }
34805
34806         /*
34807         for(var i = 0; i < colCount; i++){
34808             if(cm.isHidden(i)){
34809                 this.hideColumn(i);
34810             }
34811             if(cm.config[i].align){
34812                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34813                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34814             }
34815         }*/
34816         
34817         this.updateHeaderSortState();
34818
34819         this.beforeInitialResize();
34820         this.layout(true);
34821
34822         // two part rendering gives faster view to the user
34823         this.renderPhase2.defer(1, this);
34824     },
34825
34826     renderPhase2 : function(){
34827         // render the rows now
34828         this.refresh();
34829         if(this.grid.autoSizeColumns){
34830             this.autoSizeColumns();
34831         }
34832     },
34833
34834     beforeInitialResize : function(){
34835
34836     },
34837
34838     onColumnSplitterMoved : function(i, w){
34839         this.userResized = true;
34840         var cm = this.grid.colModel;
34841         cm.setColumnWidth(i, w, true);
34842         var cid = cm.getColumnId(i);
34843         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34844         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34845         this.updateSplitters();
34846         this.layout();
34847         this.grid.fireEvent("columnresize", i, w);
34848     },
34849
34850     syncRowHeights : function(startIndex, endIndex){
34851         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34852             startIndex = startIndex || 0;
34853             var mrows = this.getBodyTable().rows;
34854             var lrows = this.getLockedTable().rows;
34855             var len = mrows.length-1;
34856             endIndex = Math.min(endIndex || len, len);
34857             for(var i = startIndex; i <= endIndex; i++){
34858                 var m = mrows[i], l = lrows[i];
34859                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34860                 m.style.height = l.style.height = h + "px";
34861             }
34862         }
34863     },
34864
34865     layout : function(initialRender, is2ndPass)
34866     {
34867         var g = this.grid;
34868         var auto = g.autoHeight;
34869         var scrollOffset = 16;
34870         var c = g.getGridEl(), cm = this.cm,
34871                 expandCol = g.autoExpandColumn,
34872                 gv = this;
34873         //c.beginMeasure();
34874
34875         if(!c.dom.offsetWidth){ // display:none?
34876             if(initialRender){
34877                 this.lockedWrap.show();
34878                 this.mainWrap.show();
34879             }
34880             return;
34881         }
34882
34883         var hasLock = this.cm.isLocked(0);
34884
34885         var tbh = this.headerPanel.getHeight();
34886         var bbh = this.footerPanel.getHeight();
34887
34888         if(auto){
34889             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34890             var newHeight = ch + c.getBorderWidth("tb");
34891             if(g.maxHeight){
34892                 newHeight = Math.min(g.maxHeight, newHeight);
34893             }
34894             c.setHeight(newHeight);
34895         }
34896
34897         if(g.autoWidth){
34898             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34899         }
34900
34901         var s = this.scroller;
34902
34903         var csize = c.getSize(true);
34904
34905         this.el.setSize(csize.width, csize.height);
34906
34907         this.headerPanel.setWidth(csize.width);
34908         this.footerPanel.setWidth(csize.width);
34909
34910         var hdHeight = this.mainHd.getHeight();
34911         var vw = csize.width;
34912         var vh = csize.height - (tbh + bbh);
34913
34914         s.setSize(vw, vh);
34915
34916         var bt = this.getBodyTable();
34917         
34918         if(cm.getLockedCount() == cm.config.length){
34919             bt = this.getLockedTable();
34920         }
34921         
34922         var ltWidth = hasLock ?
34923                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34924
34925         var scrollHeight = bt.offsetHeight;
34926         var scrollWidth = ltWidth + bt.offsetWidth;
34927         var vscroll = false, hscroll = false;
34928
34929         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34930
34931         var lw = this.lockedWrap, mw = this.mainWrap;
34932         var lb = this.lockedBody, mb = this.mainBody;
34933
34934         setTimeout(function(){
34935             var t = s.dom.offsetTop;
34936             var w = s.dom.clientWidth,
34937                 h = s.dom.clientHeight;
34938
34939             lw.setTop(t);
34940             lw.setSize(ltWidth, h);
34941
34942             mw.setLeftTop(ltWidth, t);
34943             mw.setSize(w-ltWidth, h);
34944
34945             lb.setHeight(h-hdHeight);
34946             mb.setHeight(h-hdHeight);
34947
34948             if(is2ndPass !== true && !gv.userResized && expandCol){
34949                 // high speed resize without full column calculation
34950                 
34951                 var ci = cm.getIndexById(expandCol);
34952                 if (ci < 0) {
34953                     ci = cm.findColumnIndex(expandCol);
34954                 }
34955                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34956                 var expandId = cm.getColumnId(ci);
34957                 var  tw = cm.getTotalWidth(false);
34958                 var currentWidth = cm.getColumnWidth(ci);
34959                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34960                 if(currentWidth != cw){
34961                     cm.setColumnWidth(ci, cw, true);
34962                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34963                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34964                     gv.updateSplitters();
34965                     gv.layout(false, true);
34966                 }
34967             }
34968
34969             if(initialRender){
34970                 lw.show();
34971                 mw.show();
34972             }
34973             //c.endMeasure();
34974         }, 10);
34975     },
34976
34977     onWindowResize : function(){
34978         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34979             return;
34980         }
34981         this.layout();
34982     },
34983
34984     appendFooter : function(parentEl){
34985         return null;
34986     },
34987
34988     sortAscText : "Sort Ascending",
34989     sortDescText : "Sort Descending",
34990     lockText : "Lock Column",
34991     unlockText : "Unlock Column",
34992     columnsText : "Columns",
34993  
34994     columnsWiderText : "Wider",
34995     columnsNarrowText : "Thinner"
34996 });
34997
34998
34999 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
35000     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
35001     this.proxy.el.addClass('x-grid3-col-dd');
35002 };
35003
35004 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
35005     handleMouseDown : function(e){
35006
35007     },
35008
35009     callHandleMouseDown : function(e){
35010         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
35011     }
35012 });
35013 /*
35014  * Based on:
35015  * Ext JS Library 1.1.1
35016  * Copyright(c) 2006-2007, Ext JS, LLC.
35017  *
35018  * Originally Released Under LGPL - original licence link has changed is not relivant.
35019  *
35020  * Fork - LGPL
35021  * <script type="text/javascript">
35022  */
35023  /**
35024  * @extends Roo.dd.DDProxy
35025  * @class Roo.grid.SplitDragZone
35026  * Support for Column Header resizing
35027  * @constructor
35028  * @param {Object} config
35029  */
35030 // private
35031 // This is a support class used internally by the Grid components
35032 Roo.grid.SplitDragZone = function(grid, hd, hd2){
35033     this.grid = grid;
35034     this.view = grid.getView();
35035     this.proxy = this.view.resizeProxy;
35036     Roo.grid.SplitDragZone.superclass.constructor.call(
35037         this,
35038         hd, // ID
35039         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
35040         {  // CONFIG
35041             dragElId : Roo.id(this.proxy.dom),
35042             resizeFrame:false
35043         }
35044     );
35045     
35046     this.setHandleElId(Roo.id(hd));
35047     if (hd2 !== false) {
35048         this.setOuterHandleElId(Roo.id(hd2));
35049     }
35050     
35051     this.scroll = false;
35052 };
35053 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
35054     fly: Roo.Element.fly,
35055
35056     b4StartDrag : function(x, y){
35057         this.view.headersDisabled = true;
35058         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
35059                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
35060         );
35061         this.proxy.setHeight(h);
35062         
35063         // for old system colWidth really stored the actual width?
35064         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
35065         // which in reality did not work.. - it worked only for fixed sizes
35066         // for resizable we need to use actual sizes.
35067         var w = this.cm.getColumnWidth(this.cellIndex);
35068         if (!this.view.mainWrap) {
35069             // bootstrap.
35070             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
35071         }
35072         
35073         
35074         
35075         // this was w-this.grid.minColumnWidth;
35076         // doesnt really make sense? - w = thie curren width or the rendered one?
35077         var minw = Math.max(w-this.grid.minColumnWidth, 0);
35078         this.resetConstraints();
35079         this.setXConstraint(minw, 1000);
35080         this.setYConstraint(0, 0);
35081         this.minX = x - minw;
35082         this.maxX = x + 1000;
35083         this.startPos = x;
35084         if (!this.view.mainWrap) { // this is Bootstrap code..
35085             this.getDragEl().style.display='block';
35086         }
35087         
35088         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
35089     },
35090
35091
35092     handleMouseDown : function(e){
35093         ev = Roo.EventObject.setEvent(e);
35094         var t = this.fly(ev.getTarget());
35095         if(t.hasClass("x-grid-split")){
35096             this.cellIndex = this.view.getCellIndex(t.dom);
35097             this.split = t.dom;
35098             this.cm = this.grid.colModel;
35099             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
35100                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
35101             }
35102         }
35103     },
35104
35105     endDrag : function(e){
35106         this.view.headersDisabled = false;
35107         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
35108         var diff = endX - this.startPos;
35109         // 
35110         var w = this.cm.getColumnWidth(this.cellIndex);
35111         if (!this.view.mainWrap) {
35112             w = 0;
35113         }
35114         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
35115     },
35116
35117     autoOffset : function(){
35118         this.setDelta(0,0);
35119     }
35120 });/*
35121  * Based on:
35122  * Ext JS Library 1.1.1
35123  * Copyright(c) 2006-2007, Ext JS, LLC.
35124  *
35125  * Originally Released Under LGPL - original licence link has changed is not relivant.
35126  *
35127  * Fork - LGPL
35128  * <script type="text/javascript">
35129  */
35130  
35131 // private
35132 // This is a support class used internally by the Grid components
35133 Roo.grid.GridDragZone = function(grid, config){
35134     this.view = grid.getView();
35135     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
35136     if(this.view.lockedBody){
35137         this.setHandleElId(Roo.id(this.view.mainBody.dom));
35138         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
35139     }
35140     this.scroll = false;
35141     this.grid = grid;
35142     this.ddel = document.createElement('div');
35143     this.ddel.className = 'x-grid-dd-wrap';
35144 };
35145
35146 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
35147     ddGroup : "GridDD",
35148
35149     getDragData : function(e){
35150         var t = Roo.lib.Event.getTarget(e);
35151         var rowIndex = this.view.findRowIndex(t);
35152         var sm = this.grid.selModel;
35153             
35154         //Roo.log(rowIndex);
35155         
35156         if (sm.getSelectedCell) {
35157             // cell selection..
35158             if (!sm.getSelectedCell()) {
35159                 return false;
35160             }
35161             if (rowIndex != sm.getSelectedCell()[0]) {
35162                 return false;
35163             }
35164         
35165         }
35166         if (sm.getSelections && sm.getSelections().length < 1) {
35167             return false;
35168         }
35169         
35170         
35171         // before it used to all dragging of unseleted... - now we dont do that.
35172         if(rowIndex !== false){
35173             
35174             // if editorgrid.. 
35175             
35176             
35177             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
35178                
35179             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
35180               //  
35181             //}
35182             if (e.hasModifier()){
35183                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
35184             }
35185             
35186             Roo.log("getDragData");
35187             
35188             return {
35189                 grid: this.grid,
35190                 ddel: this.ddel,
35191                 rowIndex: rowIndex,
35192                 selections: sm.getSelections ? sm.getSelections() : (
35193                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
35194             };
35195         }
35196         return false;
35197     },
35198     
35199     
35200     onInitDrag : function(e){
35201         var data = this.dragData;
35202         this.ddel.innerHTML = this.grid.getDragDropText();
35203         this.proxy.update(this.ddel);
35204         // fire start drag?
35205     },
35206
35207     afterRepair : function(){
35208         this.dragging = false;
35209     },
35210
35211     getRepairXY : function(e, data){
35212         return false;
35213     },
35214
35215     onEndDrag : function(data, e){
35216         // fire end drag?
35217     },
35218
35219     onValidDrop : function(dd, e, id){
35220         // fire drag drop?
35221         this.hideProxy();
35222     },
35223
35224     beforeInvalidDrop : function(e, id){
35225
35226     }
35227 });/*
35228  * Based on:
35229  * Ext JS Library 1.1.1
35230  * Copyright(c) 2006-2007, Ext JS, LLC.
35231  *
35232  * Originally Released Under LGPL - original licence link has changed is not relivant.
35233  *
35234  * Fork - LGPL
35235  * <script type="text/javascript">
35236  */
35237  
35238
35239 /**
35240  * @class Roo.grid.ColumnModel
35241  * @extends Roo.util.Observable
35242  * This is the default implementation of a ColumnModel used by the Grid. It defines
35243  * the columns in the grid.
35244  * <br>Usage:<br>
35245  <pre><code>
35246  var colModel = new Roo.grid.ColumnModel([
35247         {header: "Ticker", width: 60, sortable: true, locked: true},
35248         {header: "Company Name", width: 150, sortable: true},
35249         {header: "Market Cap.", width: 100, sortable: true},
35250         {header: "$ Sales", width: 100, sortable: true, renderer: money},
35251         {header: "Employees", width: 100, sortable: true, resizable: false}
35252  ]);
35253  </code></pre>
35254  * <p>
35255  
35256  * The config options listed for this class are options which may appear in each
35257  * individual column definition.
35258  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35259  * @constructor
35260  * @param {Object} config An Array of column config objects. See this class's
35261  * config objects for details.
35262 */
35263 Roo.grid.ColumnModel = function(config){
35264         /**
35265      * The config passed into the constructor
35266      */
35267     this.config = []; //config;
35268     this.lookup = {};
35269
35270     // if no id, create one
35271     // if the column does not have a dataIndex mapping,
35272     // map it to the order it is in the config
35273     for(var i = 0, len = config.length; i < len; i++){
35274         this.addColumn(config[i]);
35275         
35276     }
35277
35278     /**
35279      * The width of columns which have no width specified (defaults to 100)
35280      * @type Number
35281      */
35282     this.defaultWidth = 100;
35283
35284     /**
35285      * Default sortable of columns which have no sortable specified (defaults to false)
35286      * @type Boolean
35287      */
35288     this.defaultSortable = false;
35289
35290     this.addEvents({
35291         /**
35292              * @event widthchange
35293              * Fires when the width of a column changes.
35294              * @param {ColumnModel} this
35295              * @param {Number} columnIndex The column index
35296              * @param {Number} newWidth The new width
35297              */
35298             "widthchange": true,
35299         /**
35300              * @event headerchange
35301              * Fires when the text of a header changes.
35302              * @param {ColumnModel} this
35303              * @param {Number} columnIndex The column index
35304              * @param {Number} newText The new header text
35305              */
35306             "headerchange": true,
35307         /**
35308              * @event hiddenchange
35309              * Fires when a column is hidden or "unhidden".
35310              * @param {ColumnModel} this
35311              * @param {Number} columnIndex The column index
35312              * @param {Boolean} hidden true if hidden, false otherwise
35313              */
35314             "hiddenchange": true,
35315             /**
35316          * @event columnmoved
35317          * Fires when a column is moved.
35318          * @param {ColumnModel} this
35319          * @param {Number} oldIndex
35320          * @param {Number} newIndex
35321          */
35322         "columnmoved" : true,
35323         /**
35324          * @event columlockchange
35325          * Fires when a column's locked state is changed
35326          * @param {ColumnModel} this
35327          * @param {Number} colIndex
35328          * @param {Boolean} locked true if locked
35329          */
35330         "columnlockchange" : true
35331     });
35332     Roo.grid.ColumnModel.superclass.constructor.call(this);
35333 };
35334 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35335     /**
35336      * @cfg {String} header The header text to display in the Grid view.
35337      */
35338         /**
35339      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
35340      */
35341         /**
35342      * @cfg {String} smHeader Header at Bootsrap Small width
35343      */
35344         /**
35345      * @cfg {String} mdHeader Header at Bootsrap Medium width
35346      */
35347         /**
35348      * @cfg {String} lgHeader Header at Bootsrap Large width
35349      */
35350         /**
35351      * @cfg {String} xlHeader Header at Bootsrap extra Large width
35352      */
35353     /**
35354      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35355      * {@link Roo.data.Record} definition from which to draw the column's value. If not
35356      * specified, the column's index is used as an index into the Record's data Array.
35357      */
35358     /**
35359      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35360      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35361      */
35362     /**
35363      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35364      * Defaults to the value of the {@link #defaultSortable} property.
35365      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35366      */
35367     /**
35368      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
35369      */
35370     /**
35371      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
35372      */
35373     /**
35374      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35375      */
35376     /**
35377      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35378      */
35379     /**
35380      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35381      * given the cell's data value. See {@link #setRenderer}. If not specified, the
35382      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
35383      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
35384      */
35385        /**
35386      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
35387      */
35388     /**
35389      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
35390      */
35391     /**
35392      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
35393      */
35394     /**
35395      * @cfg {String} cursor (Optional)
35396      */
35397     /**
35398      * @cfg {String} tooltip (Optional)
35399      */
35400     /**
35401      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
35402      */
35403     /**
35404      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
35405      */
35406     /**
35407      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
35408      */
35409     /**
35410      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
35411      */
35412         /**
35413      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
35414      */
35415     /**
35416      * Returns the id of the column at the specified index.
35417      * @param {Number} index The column index
35418      * @return {String} the id
35419      */
35420     getColumnId : function(index){
35421         return this.config[index].id;
35422     },
35423
35424     /**
35425      * Returns the column for a specified id.
35426      * @param {String} id The column id
35427      * @return {Object} the column
35428      */
35429     getColumnById : function(id){
35430         return this.lookup[id];
35431     },
35432
35433     
35434     /**
35435      * Returns the column Object for a specified dataIndex.
35436      * @param {String} dataIndex The column dataIndex
35437      * @return {Object|Boolean} the column or false if not found
35438      */
35439     getColumnByDataIndex: function(dataIndex){
35440         var index = this.findColumnIndex(dataIndex);
35441         return index > -1 ? this.config[index] : false;
35442     },
35443     
35444     /**
35445      * Returns the index for a specified column id.
35446      * @param {String} id The column id
35447      * @return {Number} the index, or -1 if not found
35448      */
35449     getIndexById : function(id){
35450         for(var i = 0, len = this.config.length; i < len; i++){
35451             if(this.config[i].id == id){
35452                 return i;
35453             }
35454         }
35455         return -1;
35456     },
35457     
35458     /**
35459      * Returns the index for a specified column dataIndex.
35460      * @param {String} dataIndex The column dataIndex
35461      * @return {Number} the index, or -1 if not found
35462      */
35463     
35464     findColumnIndex : function(dataIndex){
35465         for(var i = 0, len = this.config.length; i < len; i++){
35466             if(this.config[i].dataIndex == dataIndex){
35467                 return i;
35468             }
35469         }
35470         return -1;
35471     },
35472     
35473     
35474     moveColumn : function(oldIndex, newIndex){
35475         var c = this.config[oldIndex];
35476         this.config.splice(oldIndex, 1);
35477         this.config.splice(newIndex, 0, c);
35478         this.dataMap = null;
35479         this.fireEvent("columnmoved", this, oldIndex, newIndex);
35480     },
35481
35482     isLocked : function(colIndex){
35483         return this.config[colIndex].locked === true;
35484     },
35485
35486     setLocked : function(colIndex, value, suppressEvent){
35487         if(this.isLocked(colIndex) == value){
35488             return;
35489         }
35490         this.config[colIndex].locked = value;
35491         if(!suppressEvent){
35492             this.fireEvent("columnlockchange", this, colIndex, value);
35493         }
35494     },
35495
35496     getTotalLockedWidth : function(){
35497         var totalWidth = 0;
35498         for(var i = 0; i < this.config.length; i++){
35499             if(this.isLocked(i) && !this.isHidden(i)){
35500                 this.totalWidth += this.getColumnWidth(i);
35501             }
35502         }
35503         return totalWidth;
35504     },
35505
35506     getLockedCount : function(){
35507         for(var i = 0, len = this.config.length; i < len; i++){
35508             if(!this.isLocked(i)){
35509                 return i;
35510             }
35511         }
35512         
35513         return this.config.length;
35514     },
35515
35516     /**
35517      * Returns the number of columns.
35518      * @return {Number}
35519      */
35520     getColumnCount : function(visibleOnly){
35521         if(visibleOnly === true){
35522             var c = 0;
35523             for(var i = 0, len = this.config.length; i < len; i++){
35524                 if(!this.isHidden(i)){
35525                     c++;
35526                 }
35527             }
35528             return c;
35529         }
35530         return this.config.length;
35531     },
35532
35533     /**
35534      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35535      * @param {Function} fn
35536      * @param {Object} scope (optional)
35537      * @return {Array} result
35538      */
35539     getColumnsBy : function(fn, scope){
35540         var r = [];
35541         for(var i = 0, len = this.config.length; i < len; i++){
35542             var c = this.config[i];
35543             if(fn.call(scope||this, c, i) === true){
35544                 r[r.length] = c;
35545             }
35546         }
35547         return r;
35548     },
35549
35550     /**
35551      * Returns true if the specified column is sortable.
35552      * @param {Number} col The column index
35553      * @return {Boolean}
35554      */
35555     isSortable : function(col){
35556         if(typeof this.config[col].sortable == "undefined"){
35557             return this.defaultSortable;
35558         }
35559         return this.config[col].sortable;
35560     },
35561
35562     /**
35563      * Returns the rendering (formatting) function defined for the column.
35564      * @param {Number} col The column index.
35565      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35566      */
35567     getRenderer : function(col){
35568         if(!this.config[col].renderer){
35569             return Roo.grid.ColumnModel.defaultRenderer;
35570         }
35571         return this.config[col].renderer;
35572     },
35573
35574     /**
35575      * Sets the rendering (formatting) function for a column.
35576      * @param {Number} col The column index
35577      * @param {Function} fn The function to use to process the cell's raw data
35578      * to return HTML markup for the grid view. The render function is called with
35579      * the following parameters:<ul>
35580      * <li>Data value.</li>
35581      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35582      * <li>css A CSS style string to apply to the table cell.</li>
35583      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35584      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35585      * <li>Row index</li>
35586      * <li>Column index</li>
35587      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35588      */
35589     setRenderer : function(col, fn){
35590         this.config[col].renderer = fn;
35591     },
35592
35593     /**
35594      * Returns the width for the specified column.
35595      * @param {Number} col The column index
35596      * @param (optional) {String} gridSize bootstrap width size.
35597      * @return {Number}
35598      */
35599     getColumnWidth : function(col, gridSize)
35600         {
35601                 var cfg = this.config[col];
35602                 
35603                 if (typeof(gridSize) == 'undefined') {
35604                         return cfg.width * 1 || this.defaultWidth;
35605                 }
35606                 if (gridSize === false) { // if we set it..
35607                         return cfg.width || false;
35608                 }
35609                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
35610                 
35611                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
35612                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
35613                                 continue;
35614                         }
35615                         return cfg[ sizes[i] ];
35616                 }
35617                 return 1;
35618                 
35619     },
35620
35621     /**
35622      * Sets the width for a column.
35623      * @param {Number} col The column index
35624      * @param {Number} width The new width
35625      */
35626     setColumnWidth : function(col, width, suppressEvent){
35627         this.config[col].width = width;
35628         this.totalWidth = null;
35629         if(!suppressEvent){
35630              this.fireEvent("widthchange", this, col, width);
35631         }
35632     },
35633
35634     /**
35635      * Returns the total width of all columns.
35636      * @param {Boolean} includeHidden True to include hidden column widths
35637      * @return {Number}
35638      */
35639     getTotalWidth : function(includeHidden){
35640         if(!this.totalWidth){
35641             this.totalWidth = 0;
35642             for(var i = 0, len = this.config.length; i < len; i++){
35643                 if(includeHidden || !this.isHidden(i)){
35644                     this.totalWidth += this.getColumnWidth(i);
35645                 }
35646             }
35647         }
35648         return this.totalWidth;
35649     },
35650
35651     /**
35652      * Returns the header for the specified column.
35653      * @param {Number} col The column index
35654      * @return {String}
35655      */
35656     getColumnHeader : function(col){
35657         return this.config[col].header;
35658     },
35659
35660     /**
35661      * Sets the header for a column.
35662      * @param {Number} col The column index
35663      * @param {String} header The new header
35664      */
35665     setColumnHeader : function(col, header){
35666         this.config[col].header = header;
35667         this.fireEvent("headerchange", this, col, header);
35668     },
35669
35670     /**
35671      * Returns the tooltip for the specified column.
35672      * @param {Number} col The column index
35673      * @return {String}
35674      */
35675     getColumnTooltip : function(col){
35676             return this.config[col].tooltip;
35677     },
35678     /**
35679      * Sets the tooltip for a column.
35680      * @param {Number} col The column index
35681      * @param {String} tooltip The new tooltip
35682      */
35683     setColumnTooltip : function(col, tooltip){
35684             this.config[col].tooltip = tooltip;
35685     },
35686
35687     /**
35688      * Returns the dataIndex for the specified column.
35689      * @param {Number} col The column index
35690      * @return {Number}
35691      */
35692     getDataIndex : function(col){
35693         return this.config[col].dataIndex;
35694     },
35695
35696     /**
35697      * Sets the dataIndex for a column.
35698      * @param {Number} col The column index
35699      * @param {Number} dataIndex The new dataIndex
35700      */
35701     setDataIndex : function(col, dataIndex){
35702         this.config[col].dataIndex = dataIndex;
35703     },
35704
35705     
35706     
35707     /**
35708      * Returns true if the cell is editable.
35709      * @param {Number} colIndex The column index
35710      * @param {Number} rowIndex The row index - this is nto actually used..?
35711      * @return {Boolean}
35712      */
35713     isCellEditable : function(colIndex, rowIndex){
35714         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35715     },
35716
35717     /**
35718      * Returns the editor defined for the cell/column.
35719      * return false or null to disable editing.
35720      * @param {Number} colIndex The column index
35721      * @param {Number} rowIndex The row index
35722      * @return {Object}
35723      */
35724     getCellEditor : function(colIndex, rowIndex){
35725         return this.config[colIndex].editor;
35726     },
35727
35728     /**
35729      * Sets if a column is editable.
35730      * @param {Number} col The column index
35731      * @param {Boolean} editable True if the column is editable
35732      */
35733     setEditable : function(col, editable){
35734         this.config[col].editable = editable;
35735     },
35736
35737
35738     /**
35739      * Returns true if the column is hidden.
35740      * @param {Number} colIndex The column index
35741      * @return {Boolean}
35742      */
35743     isHidden : function(colIndex){
35744         return this.config[colIndex].hidden;
35745     },
35746
35747
35748     /**
35749      * Returns true if the column width cannot be changed
35750      */
35751     isFixed : function(colIndex){
35752         return this.config[colIndex].fixed;
35753     },
35754
35755     /**
35756      * Returns true if the column can be resized
35757      * @return {Boolean}
35758      */
35759     isResizable : function(colIndex){
35760         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35761     },
35762     /**
35763      * Sets if a column is hidden.
35764      * @param {Number} colIndex The column index
35765      * @param {Boolean} hidden True if the column is hidden
35766      */
35767     setHidden : function(colIndex, hidden){
35768         this.config[colIndex].hidden = hidden;
35769         this.totalWidth = null;
35770         this.fireEvent("hiddenchange", this, colIndex, hidden);
35771     },
35772
35773     /**
35774      * Sets the editor for a column.
35775      * @param {Number} col The column index
35776      * @param {Object} editor The editor object
35777      */
35778     setEditor : function(col, editor){
35779         this.config[col].editor = editor;
35780     },
35781     /**
35782      * Add a column (experimental...) - defaults to adding to the end..
35783      * @param {Object} config 
35784     */
35785     addColumn : function(c)
35786     {
35787     
35788         var i = this.config.length;
35789         this.config[i] = c;
35790         
35791         if(typeof c.dataIndex == "undefined"){
35792             c.dataIndex = i;
35793         }
35794         if(typeof c.renderer == "string"){
35795             c.renderer = Roo.util.Format[c.renderer];
35796         }
35797         if(typeof c.id == "undefined"){
35798             c.id = Roo.id();
35799         }
35800         if(c.editor && c.editor.xtype){
35801             c.editor  = Roo.factory(c.editor, Roo.grid);
35802         }
35803         if(c.editor && c.editor.isFormField){
35804             c.editor = new Roo.grid.GridEditor(c.editor);
35805         }
35806         this.lookup[c.id] = c;
35807     }
35808     
35809 });
35810
35811 Roo.grid.ColumnModel.defaultRenderer = function(value)
35812 {
35813     if(typeof value == "object") {
35814         return value;
35815     }
35816         if(typeof value == "string" && value.length < 1){
35817             return "&#160;";
35818         }
35819     
35820         return String.format("{0}", value);
35821 };
35822
35823 // Alias for backwards compatibility
35824 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35825 /*
35826  * Based on:
35827  * Ext JS Library 1.1.1
35828  * Copyright(c) 2006-2007, Ext JS, LLC.
35829  *
35830  * Originally Released Under LGPL - original licence link has changed is not relivant.
35831  *
35832  * Fork - LGPL
35833  * <script type="text/javascript">
35834  */
35835
35836 /**
35837  * @class Roo.grid.AbstractSelectionModel
35838  * @extends Roo.util.Observable
35839  * @abstract
35840  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35841  * implemented by descendant classes.  This class should not be directly instantiated.
35842  * @constructor
35843  */
35844 Roo.grid.AbstractSelectionModel = function(){
35845     this.locked = false;
35846     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35847 };
35848
35849 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35850     /** @ignore Called by the grid automatically. Do not call directly. */
35851     init : function(grid){
35852         this.grid = grid;
35853         this.initEvents();
35854     },
35855
35856     /**
35857      * Locks the selections.
35858      */
35859     lock : function(){
35860         this.locked = true;
35861     },
35862
35863     /**
35864      * Unlocks the selections.
35865      */
35866     unlock : function(){
35867         this.locked = false;
35868     },
35869
35870     /**
35871      * Returns true if the selections are locked.
35872      * @return {Boolean}
35873      */
35874     isLocked : function(){
35875         return this.locked;
35876     }
35877 });/*
35878  * Based on:
35879  * Ext JS Library 1.1.1
35880  * Copyright(c) 2006-2007, Ext JS, LLC.
35881  *
35882  * Originally Released Under LGPL - original licence link has changed is not relivant.
35883  *
35884  * Fork - LGPL
35885  * <script type="text/javascript">
35886  */
35887 /**
35888  * @extends Roo.grid.AbstractSelectionModel
35889  * @class Roo.grid.RowSelectionModel
35890  * The default SelectionModel used by {@link Roo.grid.Grid}.
35891  * It supports multiple selections and keyboard selection/navigation. 
35892  * @constructor
35893  * @param {Object} config
35894  */
35895 Roo.grid.RowSelectionModel = function(config){
35896     Roo.apply(this, config);
35897     this.selections = new Roo.util.MixedCollection(false, function(o){
35898         return o.id;
35899     });
35900
35901     this.last = false;
35902     this.lastActive = false;
35903
35904     this.addEvents({
35905         /**
35906         * @event selectionchange
35907         * Fires when the selection changes
35908         * @param {SelectionModel} this
35909         */
35910        "selectionchange" : true,
35911        /**
35912         * @event afterselectionchange
35913         * Fires after the selection changes (eg. by key press or clicking)
35914         * @param {SelectionModel} this
35915         */
35916        "afterselectionchange" : true,
35917        /**
35918         * @event beforerowselect
35919         * Fires when a row is selected being selected, return false to cancel.
35920         * @param {SelectionModel} this
35921         * @param {Number} rowIndex The selected index
35922         * @param {Boolean} keepExisting False if other selections will be cleared
35923         */
35924        "beforerowselect" : true,
35925        /**
35926         * @event rowselect
35927         * Fires when a row is selected.
35928         * @param {SelectionModel} this
35929         * @param {Number} rowIndex The selected index
35930         * @param {Roo.data.Record} r The record
35931         */
35932        "rowselect" : true,
35933        /**
35934         * @event rowdeselect
35935         * Fires when a row is deselected.
35936         * @param {SelectionModel} this
35937         * @param {Number} rowIndex The selected index
35938         */
35939         "rowdeselect" : true
35940     });
35941     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35942     this.locked = false;
35943 };
35944
35945 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35946     /**
35947      * @cfg {Boolean} singleSelect
35948      * True to allow selection of only one row at a time (defaults to false)
35949      */
35950     singleSelect : false,
35951
35952     // private
35953     initEvents : function(){
35954
35955         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35956             this.grid.on("mousedown", this.handleMouseDown, this);
35957         }else{ // allow click to work like normal
35958             this.grid.on("rowclick", this.handleDragableRowClick, this);
35959         }
35960         // bootstrap does not have a view..
35961         var view = this.grid.view ? this.grid.view : this.grid;
35962         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35963             "up" : function(e){
35964                 if(!e.shiftKey){
35965                     this.selectPrevious(e.shiftKey);
35966                 }else if(this.last !== false && this.lastActive !== false){
35967                     var last = this.last;
35968                     this.selectRange(this.last,  this.lastActive-1);
35969                     view.focusRow(this.lastActive);
35970                     if(last !== false){
35971                         this.last = last;
35972                     }
35973                 }else{
35974                     this.selectFirstRow();
35975                 }
35976                 this.fireEvent("afterselectionchange", this);
35977             },
35978             "down" : function(e){
35979                 if(!e.shiftKey){
35980                     this.selectNext(e.shiftKey);
35981                 }else if(this.last !== false && this.lastActive !== false){
35982                     var last = this.last;
35983                     this.selectRange(this.last,  this.lastActive+1);
35984                     view.focusRow(this.lastActive);
35985                     if(last !== false){
35986                         this.last = last;
35987                     }
35988                 }else{
35989                     this.selectFirstRow();
35990                 }
35991                 this.fireEvent("afterselectionchange", this);
35992             },
35993             scope: this
35994         });
35995
35996          
35997         view.on("refresh", this.onRefresh, this);
35998         view.on("rowupdated", this.onRowUpdated, this);
35999         view.on("rowremoved", this.onRemove, this);
36000     },
36001
36002     // private
36003     onRefresh : function(){
36004         var ds = this.grid.ds, i, v = this.grid.view;
36005         var s = this.selections;
36006         s.each(function(r){
36007             if((i = ds.indexOfId(r.id)) != -1){
36008                 v.onRowSelect(i);
36009                 s.add(ds.getAt(i)); // updating the selection relate data
36010             }else{
36011                 s.remove(r);
36012             }
36013         });
36014     },
36015
36016     // private
36017     onRemove : function(v, index, r){
36018         this.selections.remove(r);
36019     },
36020
36021     // private
36022     onRowUpdated : function(v, index, r){
36023         if(this.isSelected(r)){
36024             v.onRowSelect(index);
36025         }
36026     },
36027
36028     /**
36029      * Select records.
36030      * @param {Array} records The records to select
36031      * @param {Boolean} keepExisting (optional) True to keep existing selections
36032      */
36033     selectRecords : function(records, keepExisting){
36034         if(!keepExisting){
36035             this.clearSelections();
36036         }
36037         var ds = this.grid.ds;
36038         for(var i = 0, len = records.length; i < len; i++){
36039             this.selectRow(ds.indexOf(records[i]), true);
36040         }
36041     },
36042
36043     /**
36044      * Gets the number of selected rows.
36045      * @return {Number}
36046      */
36047     getCount : function(){
36048         return this.selections.length;
36049     },
36050
36051     /**
36052      * Selects the first row in the grid.
36053      */
36054     selectFirstRow : function(){
36055         this.selectRow(0);
36056     },
36057
36058     /**
36059      * Select the last row.
36060      * @param {Boolean} keepExisting (optional) True to keep existing selections
36061      */
36062     selectLastRow : function(keepExisting){
36063         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
36064     },
36065
36066     /**
36067      * Selects the row immediately following the last selected row.
36068      * @param {Boolean} keepExisting (optional) True to keep existing selections
36069      */
36070     selectNext : function(keepExisting){
36071         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
36072             this.selectRow(this.last+1, keepExisting);
36073             var view = this.grid.view ? this.grid.view : this.grid;
36074             view.focusRow(this.last);
36075         }
36076     },
36077
36078     /**
36079      * Selects the row that precedes the last selected row.
36080      * @param {Boolean} keepExisting (optional) True to keep existing selections
36081      */
36082     selectPrevious : function(keepExisting){
36083         if(this.last){
36084             this.selectRow(this.last-1, keepExisting);
36085             var view = this.grid.view ? this.grid.view : this.grid;
36086             view.focusRow(this.last);
36087         }
36088     },
36089
36090     /**
36091      * Returns the selected records
36092      * @return {Array} Array of selected records
36093      */
36094     getSelections : function(){
36095         return [].concat(this.selections.items);
36096     },
36097
36098     /**
36099      * Returns the first selected record.
36100      * @return {Record}
36101      */
36102     getSelected : function(){
36103         return this.selections.itemAt(0);
36104     },
36105
36106
36107     /**
36108      * Clears all selections.
36109      */
36110     clearSelections : function(fast){
36111         if(this.locked) {
36112             return;
36113         }
36114         if(fast !== true){
36115             var ds = this.grid.ds;
36116             var s = this.selections;
36117             s.each(function(r){
36118                 this.deselectRow(ds.indexOfId(r.id));
36119             }, this);
36120             s.clear();
36121         }else{
36122             this.selections.clear();
36123         }
36124         this.last = false;
36125     },
36126
36127
36128     /**
36129      * Selects all rows.
36130      */
36131     selectAll : function(){
36132         if(this.locked) {
36133             return;
36134         }
36135         this.selections.clear();
36136         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
36137             this.selectRow(i, true);
36138         }
36139     },
36140
36141     /**
36142      * Returns True if there is a selection.
36143      * @return {Boolean}
36144      */
36145     hasSelection : function(){
36146         return this.selections.length > 0;
36147     },
36148
36149     /**
36150      * Returns True if the specified row is selected.
36151      * @param {Number/Record} record The record or index of the record to check
36152      * @return {Boolean}
36153      */
36154     isSelected : function(index){
36155         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
36156         return (r && this.selections.key(r.id) ? true : false);
36157     },
36158
36159     /**
36160      * Returns True if the specified record id is selected.
36161      * @param {String} id The id of record to check
36162      * @return {Boolean}
36163      */
36164     isIdSelected : function(id){
36165         return (this.selections.key(id) ? true : false);
36166     },
36167
36168     // private
36169     handleMouseDown : function(e, t)
36170     {
36171         var view = this.grid.view ? this.grid.view : this.grid;
36172         var rowIndex;
36173         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
36174             return;
36175         };
36176         if(e.shiftKey && this.last !== false){
36177             var last = this.last;
36178             this.selectRange(last, rowIndex, e.ctrlKey);
36179             this.last = last; // reset the last
36180             view.focusRow(rowIndex);
36181         }else{
36182             var isSelected = this.isSelected(rowIndex);
36183             if(e.button !== 0 && isSelected){
36184                 view.focusRow(rowIndex);
36185             }else if(e.ctrlKey && isSelected){
36186                 this.deselectRow(rowIndex);
36187             }else if(!isSelected){
36188                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
36189                 view.focusRow(rowIndex);
36190             }
36191         }
36192         this.fireEvent("afterselectionchange", this);
36193     },
36194     // private
36195     handleDragableRowClick :  function(grid, rowIndex, e) 
36196     {
36197         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
36198             this.selectRow(rowIndex, false);
36199             var view = this.grid.view ? this.grid.view : this.grid;
36200             view.focusRow(rowIndex);
36201              this.fireEvent("afterselectionchange", this);
36202         }
36203     },
36204     
36205     /**
36206      * Selects multiple rows.
36207      * @param {Array} rows Array of the indexes of the row to select
36208      * @param {Boolean} keepExisting (optional) True to keep existing selections
36209      */
36210     selectRows : function(rows, keepExisting){
36211         if(!keepExisting){
36212             this.clearSelections();
36213         }
36214         for(var i = 0, len = rows.length; i < len; i++){
36215             this.selectRow(rows[i], true);
36216         }
36217     },
36218
36219     /**
36220      * Selects a range of rows. All rows in between startRow and endRow are also selected.
36221      * @param {Number} startRow The index of the first row in the range
36222      * @param {Number} endRow The index of the last row in the range
36223      * @param {Boolean} keepExisting (optional) True to retain existing selections
36224      */
36225     selectRange : function(startRow, endRow, keepExisting){
36226         if(this.locked) {
36227             return;
36228         }
36229         if(!keepExisting){
36230             this.clearSelections();
36231         }
36232         if(startRow <= endRow){
36233             for(var i = startRow; i <= endRow; i++){
36234                 this.selectRow(i, true);
36235             }
36236         }else{
36237             for(var i = startRow; i >= endRow; i--){
36238                 this.selectRow(i, true);
36239             }
36240         }
36241     },
36242
36243     /**
36244      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
36245      * @param {Number} startRow The index of the first row in the range
36246      * @param {Number} endRow The index of the last row in the range
36247      */
36248     deselectRange : function(startRow, endRow, preventViewNotify){
36249         if(this.locked) {
36250             return;
36251         }
36252         for(var i = startRow; i <= endRow; i++){
36253             this.deselectRow(i, preventViewNotify);
36254         }
36255     },
36256
36257     /**
36258      * Selects a row.
36259      * @param {Number} row The index of the row to select
36260      * @param {Boolean} keepExisting (optional) True to keep existing selections
36261      */
36262     selectRow : function(index, keepExisting, preventViewNotify){
36263         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
36264             return;
36265         }
36266         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36267             if(!keepExisting || this.singleSelect){
36268                 this.clearSelections();
36269             }
36270             var r = this.grid.ds.getAt(index);
36271             this.selections.add(r);
36272             this.last = this.lastActive = index;
36273             if(!preventViewNotify){
36274                 var view = this.grid.view ? this.grid.view : this.grid;
36275                 view.onRowSelect(index);
36276             }
36277             this.fireEvent("rowselect", this, index, r);
36278             this.fireEvent("selectionchange", this);
36279         }
36280     },
36281
36282     /**
36283      * Deselects a row.
36284      * @param {Number} row The index of the row to deselect
36285      */
36286     deselectRow : function(index, preventViewNotify){
36287         if(this.locked) {
36288             return;
36289         }
36290         if(this.last == index){
36291             this.last = false;
36292         }
36293         if(this.lastActive == index){
36294             this.lastActive = false;
36295         }
36296         var r = this.grid.ds.getAt(index);
36297         this.selections.remove(r);
36298         if(!preventViewNotify){
36299             var view = this.grid.view ? this.grid.view : this.grid;
36300             view.onRowDeselect(index);
36301         }
36302         this.fireEvent("rowdeselect", this, index);
36303         this.fireEvent("selectionchange", this);
36304     },
36305
36306     // private
36307     restoreLast : function(){
36308         if(this._last){
36309             this.last = this._last;
36310         }
36311     },
36312
36313     // private
36314     acceptsNav : function(row, col, cm){
36315         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36316     },
36317
36318     // private
36319     onEditorKey : function(field, e){
36320         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36321         if(k == e.TAB){
36322             e.stopEvent();
36323             ed.completeEdit();
36324             if(e.shiftKey){
36325                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36326             }else{
36327                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36328             }
36329         }else if(k == e.ENTER && !e.ctrlKey){
36330             e.stopEvent();
36331             ed.completeEdit();
36332             if(e.shiftKey){
36333                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36334             }else{
36335                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36336             }
36337         }else if(k == e.ESC){
36338             ed.cancelEdit();
36339         }
36340         if(newCell){
36341             g.startEditing(newCell[0], newCell[1]);
36342         }
36343     }
36344 });/*
36345  * Based on:
36346  * Ext JS Library 1.1.1
36347  * Copyright(c) 2006-2007, Ext JS, LLC.
36348  *
36349  * Originally Released Under LGPL - original licence link has changed is not relivant.
36350  *
36351  * Fork - LGPL
36352  * <script type="text/javascript">
36353  */
36354 /**
36355  * @class Roo.grid.CellSelectionModel
36356  * @extends Roo.grid.AbstractSelectionModel
36357  * This class provides the basic implementation for cell selection in a grid.
36358  * @constructor
36359  * @param {Object} config The object containing the configuration of this model.
36360  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36361  */
36362 Roo.grid.CellSelectionModel = function(config){
36363     Roo.apply(this, config);
36364
36365     this.selection = null;
36366
36367     this.addEvents({
36368         /**
36369              * @event beforerowselect
36370              * Fires before a cell is selected.
36371              * @param {SelectionModel} this
36372              * @param {Number} rowIndex The selected row index
36373              * @param {Number} colIndex The selected cell index
36374              */
36375             "beforecellselect" : true,
36376         /**
36377              * @event cellselect
36378              * Fires when a cell is selected.
36379              * @param {SelectionModel} this
36380              * @param {Number} rowIndex The selected row index
36381              * @param {Number} colIndex The selected cell index
36382              */
36383             "cellselect" : true,
36384         /**
36385              * @event selectionchange
36386              * Fires when the active selection changes.
36387              * @param {SelectionModel} this
36388              * @param {Object} selection null for no selection or an object (o) with two properties
36389                 <ul>
36390                 <li>o.record: the record object for the row the selection is in</li>
36391                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36392                 </ul>
36393              */
36394             "selectionchange" : true,
36395         /**
36396              * @event tabend
36397              * Fires when the tab (or enter) was pressed on the last editable cell
36398              * You can use this to trigger add new row.
36399              * @param {SelectionModel} this
36400              */
36401             "tabend" : true,
36402          /**
36403              * @event beforeeditnext
36404              * Fires before the next editable sell is made active
36405              * You can use this to skip to another cell or fire the tabend
36406              *    if you set cell to false
36407              * @param {Object} eventdata object : { cell : [ row, col ] } 
36408              */
36409             "beforeeditnext" : true
36410     });
36411     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36412 };
36413
36414 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
36415     
36416     enter_is_tab: false,
36417
36418     /** @ignore */
36419     initEvents : function(){
36420         this.grid.on("mousedown", this.handleMouseDown, this);
36421         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36422         var view = this.grid.view;
36423         view.on("refresh", this.onViewChange, this);
36424         view.on("rowupdated", this.onRowUpdated, this);
36425         view.on("beforerowremoved", this.clearSelections, this);
36426         view.on("beforerowsinserted", this.clearSelections, this);
36427         if(this.grid.isEditor){
36428             this.grid.on("beforeedit", this.beforeEdit,  this);
36429         }
36430     },
36431
36432         //private
36433     beforeEdit : function(e){
36434         this.select(e.row, e.column, false, true, e.record);
36435     },
36436
36437         //private
36438     onRowUpdated : function(v, index, r){
36439         if(this.selection && this.selection.record == r){
36440             v.onCellSelect(index, this.selection.cell[1]);
36441         }
36442     },
36443
36444         //private
36445     onViewChange : function(){
36446         this.clearSelections(true);
36447     },
36448
36449         /**
36450          * Returns the currently selected cell,.
36451          * @return {Array} The selected cell (row, column) or null if none selected.
36452          */
36453     getSelectedCell : function(){
36454         return this.selection ? this.selection.cell : null;
36455     },
36456
36457     /**
36458      * Clears all selections.
36459      * @param {Boolean} true to prevent the gridview from being notified about the change.
36460      */
36461     clearSelections : function(preventNotify){
36462         var s = this.selection;
36463         if(s){
36464             if(preventNotify !== true){
36465                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36466             }
36467             this.selection = null;
36468             this.fireEvent("selectionchange", this, null);
36469         }
36470     },
36471
36472     /**
36473      * Returns true if there is a selection.
36474      * @return {Boolean}
36475      */
36476     hasSelection : function(){
36477         return this.selection ? true : false;
36478     },
36479
36480     /** @ignore */
36481     handleMouseDown : function(e, t){
36482         var v = this.grid.getView();
36483         if(this.isLocked()){
36484             return;
36485         };
36486         var row = v.findRowIndex(t);
36487         var cell = v.findCellIndex(t);
36488         if(row !== false && cell !== false){
36489             this.select(row, cell);
36490         }
36491     },
36492
36493     /**
36494      * Selects a cell.
36495      * @param {Number} rowIndex
36496      * @param {Number} collIndex
36497      */
36498     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36499         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36500             this.clearSelections();
36501             r = r || this.grid.dataSource.getAt(rowIndex);
36502             this.selection = {
36503                 record : r,
36504                 cell : [rowIndex, colIndex]
36505             };
36506             if(!preventViewNotify){
36507                 var v = this.grid.getView();
36508                 v.onCellSelect(rowIndex, colIndex);
36509                 if(preventFocus !== true){
36510                     v.focusCell(rowIndex, colIndex);
36511                 }
36512             }
36513             this.fireEvent("cellselect", this, rowIndex, colIndex);
36514             this.fireEvent("selectionchange", this, this.selection);
36515         }
36516     },
36517
36518         //private
36519     isSelectable : function(rowIndex, colIndex, cm){
36520         return !cm.isHidden(colIndex);
36521     },
36522
36523     /** @ignore */
36524     handleKeyDown : function(e){
36525         //Roo.log('Cell Sel Model handleKeyDown');
36526         if(!e.isNavKeyPress()){
36527             return;
36528         }
36529         var g = this.grid, s = this.selection;
36530         if(!s){
36531             e.stopEvent();
36532             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
36533             if(cell){
36534                 this.select(cell[0], cell[1]);
36535             }
36536             return;
36537         }
36538         var sm = this;
36539         var walk = function(row, col, step){
36540             return g.walkCells(row, col, step, sm.isSelectable,  sm);
36541         };
36542         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36543         var newCell;
36544
36545       
36546
36547         switch(k){
36548             case e.TAB:
36549                 // handled by onEditorKey
36550                 if (g.isEditor && g.editing) {
36551                     return;
36552                 }
36553                 if(e.shiftKey) {
36554                     newCell = walk(r, c-1, -1);
36555                 } else {
36556                     newCell = walk(r, c+1, 1);
36557                 }
36558                 break;
36559             
36560             case e.DOWN:
36561                newCell = walk(r+1, c, 1);
36562                 break;
36563             
36564             case e.UP:
36565                 newCell = walk(r-1, c, -1);
36566                 break;
36567             
36568             case e.RIGHT:
36569                 newCell = walk(r, c+1, 1);
36570                 break;
36571             
36572             case e.LEFT:
36573                 newCell = walk(r, c-1, -1);
36574                 break;
36575             
36576             case e.ENTER:
36577                 
36578                 if(g.isEditor && !g.editing){
36579                    g.startEditing(r, c);
36580                    e.stopEvent();
36581                    return;
36582                 }
36583                 
36584                 
36585              break;
36586         };
36587         if(newCell){
36588             this.select(newCell[0], newCell[1]);
36589             e.stopEvent();
36590             
36591         }
36592     },
36593
36594     acceptsNav : function(row, col, cm){
36595         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36596     },
36597     /**
36598      * Selects a cell.
36599      * @param {Number} field (not used) - as it's normally used as a listener
36600      * @param {Number} e - event - fake it by using
36601      *
36602      * var e = Roo.EventObjectImpl.prototype;
36603      * e.keyCode = e.TAB
36604      *
36605      * 
36606      */
36607     onEditorKey : function(field, e){
36608         
36609         var k = e.getKey(),
36610             newCell,
36611             g = this.grid,
36612             ed = g.activeEditor,
36613             forward = false;
36614         ///Roo.log('onEditorKey' + k);
36615         
36616         
36617         if (this.enter_is_tab && k == e.ENTER) {
36618             k = e.TAB;
36619         }
36620         
36621         if(k == e.TAB){
36622             if(e.shiftKey){
36623                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36624             }else{
36625                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36626                 forward = true;
36627             }
36628             
36629             e.stopEvent();
36630             
36631         } else if(k == e.ENTER &&  !e.ctrlKey){
36632             ed.completeEdit();
36633             e.stopEvent();
36634             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36635         
36636                 } else if(k == e.ESC){
36637             ed.cancelEdit();
36638         }
36639                 
36640         if (newCell) {
36641             var ecall = { cell : newCell, forward : forward };
36642             this.fireEvent('beforeeditnext', ecall );
36643             newCell = ecall.cell;
36644                         forward = ecall.forward;
36645         }
36646                 
36647         if(newCell){
36648             //Roo.log('next cell after edit');
36649             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36650         } else if (forward) {
36651             // tabbed past last
36652             this.fireEvent.defer(100, this, ['tabend',this]);
36653         }
36654     }
36655 });/*
36656  * Based on:
36657  * Ext JS Library 1.1.1
36658  * Copyright(c) 2006-2007, Ext JS, LLC.
36659  *
36660  * Originally Released Under LGPL - original licence link has changed is not relivant.
36661  *
36662  * Fork - LGPL
36663  * <script type="text/javascript">
36664  */
36665  
36666 /**
36667  * @class Roo.grid.EditorGrid
36668  * @extends Roo.grid.Grid
36669  * Class for creating and editable grid.
36670  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36671  * The container MUST have some type of size defined for the grid to fill. The container will be 
36672  * automatically set to position relative if it isn't already.
36673  * @param {Object} dataSource The data model to bind to
36674  * @param {Object} colModel The column model with info about this grid's columns
36675  */
36676 Roo.grid.EditorGrid = function(container, config){
36677     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36678     this.getGridEl().addClass("xedit-grid");
36679
36680     if(!this.selModel){
36681         this.selModel = new Roo.grid.CellSelectionModel();
36682     }
36683
36684     this.activeEditor = null;
36685
36686         this.addEvents({
36687             /**
36688              * @event beforeedit
36689              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36690              * <ul style="padding:5px;padding-left:16px;">
36691              * <li>grid - This grid</li>
36692              * <li>record - The record being edited</li>
36693              * <li>field - The field name being edited</li>
36694              * <li>value - The value for the field being edited.</li>
36695              * <li>row - The grid row index</li>
36696              * <li>column - The grid column index</li>
36697              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36698              * </ul>
36699              * @param {Object} e An edit event (see above for description)
36700              */
36701             "beforeedit" : true,
36702             /**
36703              * @event afteredit
36704              * Fires after a cell is edited. <br />
36705              * <ul style="padding:5px;padding-left:16px;">
36706              * <li>grid - This grid</li>
36707              * <li>record - The record being edited</li>
36708              * <li>field - The field name being edited</li>
36709              * <li>value - The value being set</li>
36710              * <li>originalValue - The original value for the field, before the edit.</li>
36711              * <li>row - The grid row index</li>
36712              * <li>column - The grid column index</li>
36713              * </ul>
36714              * @param {Object} e An edit event (see above for description)
36715              */
36716             "afteredit" : true,
36717             /**
36718              * @event validateedit
36719              * Fires after a cell is edited, but before the value is set in the record. 
36720          * You can use this to modify the value being set in the field, Return false
36721              * to cancel the change. The edit event object has the following properties <br />
36722              * <ul style="padding:5px;padding-left:16px;">
36723          * <li>editor - This editor</li>
36724              * <li>grid - This grid</li>
36725              * <li>record - The record being edited</li>
36726              * <li>field - The field name being edited</li>
36727              * <li>value - The value being set</li>
36728              * <li>originalValue - The original value for the field, before the edit.</li>
36729              * <li>row - The grid row index</li>
36730              * <li>column - The grid column index</li>
36731              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36732              * </ul>
36733              * @param {Object} e An edit event (see above for description)
36734              */
36735             "validateedit" : true
36736         });
36737     this.on("bodyscroll", this.stopEditing,  this);
36738     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36739 };
36740
36741 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36742     /**
36743      * @cfg {Number} clicksToEdit
36744      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36745      */
36746     clicksToEdit: 2,
36747
36748     // private
36749     isEditor : true,
36750     // private
36751     trackMouseOver: false, // causes very odd FF errors
36752
36753     onCellDblClick : function(g, row, col){
36754         this.startEditing(row, col);
36755     },
36756
36757     onEditComplete : function(ed, value, startValue){
36758         this.editing = false;
36759         this.activeEditor = null;
36760         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36761         var r = ed.record;
36762         var field = this.colModel.getDataIndex(ed.col);
36763         var e = {
36764             grid: this,
36765             record: r,
36766             field: field,
36767             originalValue: startValue,
36768             value: value,
36769             row: ed.row,
36770             column: ed.col,
36771             cancel:false,
36772             editor: ed
36773         };
36774         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36775         cell.show();
36776           
36777         if(String(value) !== String(startValue)){
36778             
36779             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36780                 r.set(field, e.value);
36781                 // if we are dealing with a combo box..
36782                 // then we also set the 'name' colum to be the displayField
36783                 if (ed.field.displayField && ed.field.name) {
36784                     r.set(ed.field.name, ed.field.el.dom.value);
36785                 }
36786                 
36787                 delete e.cancel; //?? why!!!
36788                 this.fireEvent("afteredit", e);
36789             }
36790         } else {
36791             this.fireEvent("afteredit", e); // always fire it!
36792         }
36793         this.view.focusCell(ed.row, ed.col);
36794     },
36795
36796     /**
36797      * Starts editing the specified for the specified row/column
36798      * @param {Number} rowIndex
36799      * @param {Number} colIndex
36800      */
36801     startEditing : function(row, col){
36802         this.stopEditing();
36803         if(this.colModel.isCellEditable(col, row)){
36804             this.view.ensureVisible(row, col, true);
36805           
36806             var r = this.dataSource.getAt(row);
36807             var field = this.colModel.getDataIndex(col);
36808             var cell = Roo.get(this.view.getCell(row,col));
36809             var e = {
36810                 grid: this,
36811                 record: r,
36812                 field: field,
36813                 value: r.data[field],
36814                 row: row,
36815                 column: col,
36816                 cancel:false 
36817             };
36818             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36819                 this.editing = true;
36820                 var ed = this.colModel.getCellEditor(col, row);
36821                 
36822                 if (!ed) {
36823                     return;
36824                 }
36825                 if(!ed.rendered){
36826                     ed.render(ed.parentEl || document.body);
36827                 }
36828                 ed.field.reset();
36829                
36830                 cell.hide();
36831                 
36832                 (function(){ // complex but required for focus issues in safari, ie and opera
36833                     ed.row = row;
36834                     ed.col = col;
36835                     ed.record = r;
36836                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
36837                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
36838                     this.activeEditor = ed;
36839                     var v = r.data[field];
36840                     ed.startEdit(this.view.getCell(row, col), v);
36841                     // combo's with 'displayField and name set
36842                     if (ed.field.displayField && ed.field.name) {
36843                         ed.field.el.dom.value = r.data[ed.field.name];
36844                     }
36845                     
36846                     
36847                 }).defer(50, this);
36848             }
36849         }
36850     },
36851         
36852     /**
36853      * Stops any active editing
36854      */
36855     stopEditing : function(){
36856         if(this.activeEditor){
36857             this.activeEditor.completeEdit();
36858         }
36859         this.activeEditor = null;
36860     },
36861         
36862          /**
36863      * Called to get grid's drag proxy text, by default returns this.ddText.
36864      * @return {String}
36865      */
36866     getDragDropText : function(){
36867         var count = this.selModel.getSelectedCell() ? 1 : 0;
36868         return String.format(this.ddText, count, count == 1 ? '' : 's');
36869     }
36870         
36871 });/*
36872  * Based on:
36873  * Ext JS Library 1.1.1
36874  * Copyright(c) 2006-2007, Ext JS, LLC.
36875  *
36876  * Originally Released Under LGPL - original licence link has changed is not relivant.
36877  *
36878  * Fork - LGPL
36879  * <script type="text/javascript">
36880  */
36881
36882 // private - not really -- you end up using it !
36883 // This is a support class used internally by the Grid components
36884
36885 /**
36886  * @class Roo.grid.GridEditor
36887  * @extends Roo.Editor
36888  * Class for creating and editable grid elements.
36889  * @param {Object} config any settings (must include field)
36890  */
36891 Roo.grid.GridEditor = function(field, config){
36892     if (!config && field.field) {
36893         config = field;
36894         field = Roo.factory(config.field, Roo.form);
36895     }
36896     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36897     field.monitorTab = false;
36898 };
36899
36900 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36901     
36902     /**
36903      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36904      */
36905     
36906     alignment: "tl-tl",
36907     autoSize: "width",
36908     hideEl : false,
36909     cls: "x-small-editor x-grid-editor",
36910     shim:false,
36911     shadow:"frame"
36912 });/*
36913  * Based on:
36914  * Ext JS Library 1.1.1
36915  * Copyright(c) 2006-2007, Ext JS, LLC.
36916  *
36917  * Originally Released Under LGPL - original licence link has changed is not relivant.
36918  *
36919  * Fork - LGPL
36920  * <script type="text/javascript">
36921  */
36922   
36923
36924   
36925 Roo.grid.PropertyRecord = Roo.data.Record.create([
36926     {name:'name',type:'string'},  'value'
36927 ]);
36928
36929
36930 Roo.grid.PropertyStore = function(grid, source){
36931     this.grid = grid;
36932     this.store = new Roo.data.Store({
36933         recordType : Roo.grid.PropertyRecord
36934     });
36935     this.store.on('update', this.onUpdate,  this);
36936     if(source){
36937         this.setSource(source);
36938     }
36939     Roo.grid.PropertyStore.superclass.constructor.call(this);
36940 };
36941
36942
36943
36944 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36945     setSource : function(o){
36946         this.source = o;
36947         this.store.removeAll();
36948         var data = [];
36949         for(var k in o){
36950             if(this.isEditableValue(o[k])){
36951                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36952             }
36953         }
36954         this.store.loadRecords({records: data}, {}, true);
36955     },
36956
36957     onUpdate : function(ds, record, type){
36958         if(type == Roo.data.Record.EDIT){
36959             var v = record.data['value'];
36960             var oldValue = record.modified['value'];
36961             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36962                 this.source[record.id] = v;
36963                 record.commit();
36964                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36965             }else{
36966                 record.reject();
36967             }
36968         }
36969     },
36970
36971     getProperty : function(row){
36972        return this.store.getAt(row);
36973     },
36974
36975     isEditableValue: function(val){
36976         if(val && val instanceof Date){
36977             return true;
36978         }else if(typeof val == 'object' || typeof val == 'function'){
36979             return false;
36980         }
36981         return true;
36982     },
36983
36984     setValue : function(prop, value){
36985         this.source[prop] = value;
36986         this.store.getById(prop).set('value', value);
36987     },
36988
36989     getSource : function(){
36990         return this.source;
36991     }
36992 });
36993
36994 Roo.grid.PropertyColumnModel = function(grid, store){
36995     this.grid = grid;
36996     var g = Roo.grid;
36997     g.PropertyColumnModel.superclass.constructor.call(this, [
36998         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36999         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
37000     ]);
37001     this.store = store;
37002     this.bselect = Roo.DomHelper.append(document.body, {
37003         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
37004             {tag: 'option', value: 'true', html: 'true'},
37005             {tag: 'option', value: 'false', html: 'false'}
37006         ]
37007     });
37008     Roo.id(this.bselect);
37009     var f = Roo.form;
37010     this.editors = {
37011         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
37012         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
37013         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
37014         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
37015         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
37016     };
37017     this.renderCellDelegate = this.renderCell.createDelegate(this);
37018     this.renderPropDelegate = this.renderProp.createDelegate(this);
37019 };
37020
37021 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
37022     
37023     
37024     nameText : 'Name',
37025     valueText : 'Value',
37026     
37027     dateFormat : 'm/j/Y',
37028     
37029     
37030     renderDate : function(dateVal){
37031         return dateVal.dateFormat(this.dateFormat);
37032     },
37033
37034     renderBool : function(bVal){
37035         return bVal ? 'true' : 'false';
37036     },
37037
37038     isCellEditable : function(colIndex, rowIndex){
37039         return colIndex == 1;
37040     },
37041
37042     getRenderer : function(col){
37043         return col == 1 ?
37044             this.renderCellDelegate : this.renderPropDelegate;
37045     },
37046
37047     renderProp : function(v){
37048         return this.getPropertyName(v);
37049     },
37050
37051     renderCell : function(val){
37052         var rv = val;
37053         if(val instanceof Date){
37054             rv = this.renderDate(val);
37055         }else if(typeof val == 'boolean'){
37056             rv = this.renderBool(val);
37057         }
37058         return Roo.util.Format.htmlEncode(rv);
37059     },
37060
37061     getPropertyName : function(name){
37062         var pn = this.grid.propertyNames;
37063         return pn && pn[name] ? pn[name] : name;
37064     },
37065
37066     getCellEditor : function(colIndex, rowIndex){
37067         var p = this.store.getProperty(rowIndex);
37068         var n = p.data['name'], val = p.data['value'];
37069         
37070         if(typeof(this.grid.customEditors[n]) == 'string'){
37071             return this.editors[this.grid.customEditors[n]];
37072         }
37073         if(typeof(this.grid.customEditors[n]) != 'undefined'){
37074             return this.grid.customEditors[n];
37075         }
37076         if(val instanceof Date){
37077             return this.editors['date'];
37078         }else if(typeof val == 'number'){
37079             return this.editors['number'];
37080         }else if(typeof val == 'boolean'){
37081             return this.editors['boolean'];
37082         }else{
37083             return this.editors['string'];
37084         }
37085     }
37086 });
37087
37088 /**
37089  * @class Roo.grid.PropertyGrid
37090  * @extends Roo.grid.EditorGrid
37091  * This class represents the  interface of a component based property grid control.
37092  * <br><br>Usage:<pre><code>
37093  var grid = new Roo.grid.PropertyGrid("my-container-id", {
37094       
37095  });
37096  // set any options
37097  grid.render();
37098  * </code></pre>
37099   
37100  * @constructor
37101  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37102  * The container MUST have some type of size defined for the grid to fill. The container will be
37103  * automatically set to position relative if it isn't already.
37104  * @param {Object} config A config object that sets properties on this grid.
37105  */
37106 Roo.grid.PropertyGrid = function(container, config){
37107     config = config || {};
37108     var store = new Roo.grid.PropertyStore(this);
37109     this.store = store;
37110     var cm = new Roo.grid.PropertyColumnModel(this, store);
37111     store.store.sort('name', 'ASC');
37112     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
37113         ds: store.store,
37114         cm: cm,
37115         enableColLock:false,
37116         enableColumnMove:false,
37117         stripeRows:false,
37118         trackMouseOver: false,
37119         clicksToEdit:1
37120     }, config));
37121     this.getGridEl().addClass('x-props-grid');
37122     this.lastEditRow = null;
37123     this.on('columnresize', this.onColumnResize, this);
37124     this.addEvents({
37125          /**
37126              * @event beforepropertychange
37127              * Fires before a property changes (return false to stop?)
37128              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37129              * @param {String} id Record Id
37130              * @param {String} newval New Value
37131          * @param {String} oldval Old Value
37132              */
37133         "beforepropertychange": true,
37134         /**
37135              * @event propertychange
37136              * Fires after a property changes
37137              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37138              * @param {String} id Record Id
37139              * @param {String} newval New Value
37140          * @param {String} oldval Old Value
37141              */
37142         "propertychange": true
37143     });
37144     this.customEditors = this.customEditors || {};
37145 };
37146 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
37147     
37148      /**
37149      * @cfg {Object} customEditors map of colnames=> custom editors.
37150      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
37151      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
37152      * false disables editing of the field.
37153          */
37154     
37155       /**
37156      * @cfg {Object} propertyNames map of property Names to their displayed value
37157          */
37158     
37159     render : function(){
37160         Roo.grid.PropertyGrid.superclass.render.call(this);
37161         this.autoSize.defer(100, this);
37162     },
37163
37164     autoSize : function(){
37165         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
37166         if(this.view){
37167             this.view.fitColumns();
37168         }
37169     },
37170
37171     onColumnResize : function(){
37172         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
37173         this.autoSize();
37174     },
37175     /**
37176      * Sets the data for the Grid
37177      * accepts a Key => Value object of all the elements avaiable.
37178      * @param {Object} data  to appear in grid.
37179      */
37180     setSource : function(source){
37181         this.store.setSource(source);
37182         //this.autoSize();
37183     },
37184     /**
37185      * Gets all the data from the grid.
37186      * @return {Object} data  data stored in grid
37187      */
37188     getSource : function(){
37189         return this.store.getSource();
37190     }
37191 });/*
37192   
37193  * Licence LGPL
37194  
37195  */
37196  
37197 /**
37198  * @class Roo.grid.Calendar
37199  * @extends Roo.util.Grid
37200  * This class extends the Grid to provide a calendar widget
37201  * <br><br>Usage:<pre><code>
37202  var grid = new Roo.grid.Calendar("my-container-id", {
37203      ds: myDataStore,
37204      cm: myColModel,
37205      selModel: mySelectionModel,
37206      autoSizeColumns: true,
37207      monitorWindowResize: false,
37208      trackMouseOver: true
37209      eventstore : real data store..
37210  });
37211  // set any options
37212  grid.render();
37213   
37214   * @constructor
37215  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37216  * The container MUST have some type of size defined for the grid to fill. The container will be
37217  * automatically set to position relative if it isn't already.
37218  * @param {Object} config A config object that sets properties on this grid.
37219  */
37220 Roo.grid.Calendar = function(container, config){
37221         // initialize the container
37222         this.container = Roo.get(container);
37223         this.container.update("");
37224         this.container.setStyle("overflow", "hidden");
37225     this.container.addClass('x-grid-container');
37226
37227     this.id = this.container.id;
37228
37229     Roo.apply(this, config);
37230     // check and correct shorthanded configs
37231     
37232     var rows = [];
37233     var d =1;
37234     for (var r = 0;r < 6;r++) {
37235         
37236         rows[r]=[];
37237         for (var c =0;c < 7;c++) {
37238             rows[r][c]= '';
37239         }
37240     }
37241     if (this.eventStore) {
37242         this.eventStore= Roo.factory(this.eventStore, Roo.data);
37243         this.eventStore.on('load',this.onLoad, this);
37244         this.eventStore.on('beforeload',this.clearEvents, this);
37245          
37246     }
37247     
37248     this.dataSource = new Roo.data.Store({
37249             proxy: new Roo.data.MemoryProxy(rows),
37250             reader: new Roo.data.ArrayReader({}, [
37251                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
37252     });
37253
37254     this.dataSource.load();
37255     this.ds = this.dataSource;
37256     this.ds.xmodule = this.xmodule || false;
37257     
37258     
37259     var cellRender = function(v,x,r)
37260     {
37261         return String.format(
37262             '<div class="fc-day  fc-widget-content"><div>' +
37263                 '<div class="fc-event-container"></div>' +
37264                 '<div class="fc-day-number">{0}</div>'+
37265                 
37266                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
37267             '</div></div>', v);
37268     
37269     }
37270     
37271     
37272     this.colModel = new Roo.grid.ColumnModel( [
37273         {
37274             xtype: 'ColumnModel',
37275             xns: Roo.grid,
37276             dataIndex : 'weekday0',
37277             header : 'Sunday',
37278             renderer : cellRender
37279         },
37280         {
37281             xtype: 'ColumnModel',
37282             xns: Roo.grid,
37283             dataIndex : 'weekday1',
37284             header : 'Monday',
37285             renderer : cellRender
37286         },
37287         {
37288             xtype: 'ColumnModel',
37289             xns: Roo.grid,
37290             dataIndex : 'weekday2',
37291             header : 'Tuesday',
37292             renderer : cellRender
37293         },
37294         {
37295             xtype: 'ColumnModel',
37296             xns: Roo.grid,
37297             dataIndex : 'weekday3',
37298             header : 'Wednesday',
37299             renderer : cellRender
37300         },
37301         {
37302             xtype: 'ColumnModel',
37303             xns: Roo.grid,
37304             dataIndex : 'weekday4',
37305             header : 'Thursday',
37306             renderer : cellRender
37307         },
37308         {
37309             xtype: 'ColumnModel',
37310             xns: Roo.grid,
37311             dataIndex : 'weekday5',
37312             header : 'Friday',
37313             renderer : cellRender
37314         },
37315         {
37316             xtype: 'ColumnModel',
37317             xns: Roo.grid,
37318             dataIndex : 'weekday6',
37319             header : 'Saturday',
37320             renderer : cellRender
37321         }
37322     ]);
37323     this.cm = this.colModel;
37324     this.cm.xmodule = this.xmodule || false;
37325  
37326         
37327           
37328     //this.selModel = new Roo.grid.CellSelectionModel();
37329     //this.sm = this.selModel;
37330     //this.selModel.init(this);
37331     
37332     
37333     if(this.width){
37334         this.container.setWidth(this.width);
37335     }
37336
37337     if(this.height){
37338         this.container.setHeight(this.height);
37339     }
37340     /** @private */
37341         this.addEvents({
37342         // raw events
37343         /**
37344          * @event click
37345          * The raw click event for the entire grid.
37346          * @param {Roo.EventObject} e
37347          */
37348         "click" : true,
37349         /**
37350          * @event dblclick
37351          * The raw dblclick event for the entire grid.
37352          * @param {Roo.EventObject} e
37353          */
37354         "dblclick" : true,
37355         /**
37356          * @event contextmenu
37357          * The raw contextmenu event for the entire grid.
37358          * @param {Roo.EventObject} e
37359          */
37360         "contextmenu" : true,
37361         /**
37362          * @event mousedown
37363          * The raw mousedown event for the entire grid.
37364          * @param {Roo.EventObject} e
37365          */
37366         "mousedown" : true,
37367         /**
37368          * @event mouseup
37369          * The raw mouseup event for the entire grid.
37370          * @param {Roo.EventObject} e
37371          */
37372         "mouseup" : true,
37373         /**
37374          * @event mouseover
37375          * The raw mouseover event for the entire grid.
37376          * @param {Roo.EventObject} e
37377          */
37378         "mouseover" : true,
37379         /**
37380          * @event mouseout
37381          * The raw mouseout event for the entire grid.
37382          * @param {Roo.EventObject} e
37383          */
37384         "mouseout" : true,
37385         /**
37386          * @event keypress
37387          * The raw keypress event for the entire grid.
37388          * @param {Roo.EventObject} e
37389          */
37390         "keypress" : true,
37391         /**
37392          * @event keydown
37393          * The raw keydown event for the entire grid.
37394          * @param {Roo.EventObject} e
37395          */
37396         "keydown" : true,
37397
37398         // custom events
37399
37400         /**
37401          * @event cellclick
37402          * Fires when a cell is clicked
37403          * @param {Grid} this
37404          * @param {Number} rowIndex
37405          * @param {Number} columnIndex
37406          * @param {Roo.EventObject} e
37407          */
37408         "cellclick" : true,
37409         /**
37410          * @event celldblclick
37411          * Fires when a cell is double clicked
37412          * @param {Grid} this
37413          * @param {Number} rowIndex
37414          * @param {Number} columnIndex
37415          * @param {Roo.EventObject} e
37416          */
37417         "celldblclick" : true,
37418         /**
37419          * @event rowclick
37420          * Fires when a row is clicked
37421          * @param {Grid} this
37422          * @param {Number} rowIndex
37423          * @param {Roo.EventObject} e
37424          */
37425         "rowclick" : true,
37426         /**
37427          * @event rowdblclick
37428          * Fires when a row is double clicked
37429          * @param {Grid} this
37430          * @param {Number} rowIndex
37431          * @param {Roo.EventObject} e
37432          */
37433         "rowdblclick" : true,
37434         /**
37435          * @event headerclick
37436          * Fires when a header is clicked
37437          * @param {Grid} this
37438          * @param {Number} columnIndex
37439          * @param {Roo.EventObject} e
37440          */
37441         "headerclick" : true,
37442         /**
37443          * @event headerdblclick
37444          * Fires when a header cell is double clicked
37445          * @param {Grid} this
37446          * @param {Number} columnIndex
37447          * @param {Roo.EventObject} e
37448          */
37449         "headerdblclick" : true,
37450         /**
37451          * @event rowcontextmenu
37452          * Fires when a row is right clicked
37453          * @param {Grid} this
37454          * @param {Number} rowIndex
37455          * @param {Roo.EventObject} e
37456          */
37457         "rowcontextmenu" : true,
37458         /**
37459          * @event cellcontextmenu
37460          * Fires when a cell is right clicked
37461          * @param {Grid} this
37462          * @param {Number} rowIndex
37463          * @param {Number} cellIndex
37464          * @param {Roo.EventObject} e
37465          */
37466          "cellcontextmenu" : true,
37467         /**
37468          * @event headercontextmenu
37469          * Fires when a header is right clicked
37470          * @param {Grid} this
37471          * @param {Number} columnIndex
37472          * @param {Roo.EventObject} e
37473          */
37474         "headercontextmenu" : true,
37475         /**
37476          * @event bodyscroll
37477          * Fires when the body element is scrolled
37478          * @param {Number} scrollLeft
37479          * @param {Number} scrollTop
37480          */
37481         "bodyscroll" : true,
37482         /**
37483          * @event columnresize
37484          * Fires when the user resizes a column
37485          * @param {Number} columnIndex
37486          * @param {Number} newSize
37487          */
37488         "columnresize" : true,
37489         /**
37490          * @event columnmove
37491          * Fires when the user moves a column
37492          * @param {Number} oldIndex
37493          * @param {Number} newIndex
37494          */
37495         "columnmove" : true,
37496         /**
37497          * @event startdrag
37498          * Fires when row(s) start being dragged
37499          * @param {Grid} this
37500          * @param {Roo.GridDD} dd The drag drop object
37501          * @param {event} e The raw browser event
37502          */
37503         "startdrag" : true,
37504         /**
37505          * @event enddrag
37506          * Fires when a drag operation is complete
37507          * @param {Grid} this
37508          * @param {Roo.GridDD} dd The drag drop object
37509          * @param {event} e The raw browser event
37510          */
37511         "enddrag" : true,
37512         /**
37513          * @event dragdrop
37514          * Fires when dragged row(s) are dropped on a valid DD target
37515          * @param {Grid} this
37516          * @param {Roo.GridDD} dd The drag drop object
37517          * @param {String} targetId The target drag drop object
37518          * @param {event} e The raw browser event
37519          */
37520         "dragdrop" : true,
37521         /**
37522          * @event dragover
37523          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
37524          * @param {Grid} this
37525          * @param {Roo.GridDD} dd The drag drop object
37526          * @param {String} targetId The target drag drop object
37527          * @param {event} e The raw browser event
37528          */
37529         "dragover" : true,
37530         /**
37531          * @event dragenter
37532          *  Fires when the dragged row(s) first cross another DD target while being dragged
37533          * @param {Grid} this
37534          * @param {Roo.GridDD} dd The drag drop object
37535          * @param {String} targetId The target drag drop object
37536          * @param {event} e The raw browser event
37537          */
37538         "dragenter" : true,
37539         /**
37540          * @event dragout
37541          * Fires when the dragged row(s) leave another DD target while being dragged
37542          * @param {Grid} this
37543          * @param {Roo.GridDD} dd The drag drop object
37544          * @param {String} targetId The target drag drop object
37545          * @param {event} e The raw browser event
37546          */
37547         "dragout" : true,
37548         /**
37549          * @event rowclass
37550          * Fires when a row is rendered, so you can change add a style to it.
37551          * @param {GridView} gridview   The grid view
37552          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
37553          */
37554         'rowclass' : true,
37555
37556         /**
37557          * @event render
37558          * Fires when the grid is rendered
37559          * @param {Grid} grid
37560          */
37561         'render' : true,
37562             /**
37563              * @event select
37564              * Fires when a date is selected
37565              * @param {DatePicker} this
37566              * @param {Date} date The selected date
37567              */
37568         'select': true,
37569         /**
37570              * @event monthchange
37571              * Fires when the displayed month changes 
37572              * @param {DatePicker} this
37573              * @param {Date} date The selected month
37574              */
37575         'monthchange': true,
37576         /**
37577              * @event evententer
37578              * Fires when mouse over an event
37579              * @param {Calendar} this
37580              * @param {event} Event
37581              */
37582         'evententer': true,
37583         /**
37584              * @event eventleave
37585              * Fires when the mouse leaves an
37586              * @param {Calendar} this
37587              * @param {event}
37588              */
37589         'eventleave': true,
37590         /**
37591              * @event eventclick
37592              * Fires when the mouse click an
37593              * @param {Calendar} this
37594              * @param {event}
37595              */
37596         'eventclick': true,
37597         /**
37598              * @event eventrender
37599              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
37600              * @param {Calendar} this
37601              * @param {data} data to be modified
37602              */
37603         'eventrender': true
37604         
37605     });
37606
37607     Roo.grid.Grid.superclass.constructor.call(this);
37608     this.on('render', function() {
37609         this.view.el.addClass('x-grid-cal'); 
37610         
37611         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
37612
37613     },this);
37614     
37615     if (!Roo.grid.Calendar.style) {
37616         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
37617             
37618             
37619             '.x-grid-cal .x-grid-col' :  {
37620                 height: 'auto !important',
37621                 'vertical-align': 'top'
37622             },
37623             '.x-grid-cal  .fc-event-hori' : {
37624                 height: '14px'
37625             }
37626              
37627             
37628         }, Roo.id());
37629     }
37630
37631     
37632     
37633 };
37634 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
37635     /**
37636      * @cfg {Store} eventStore The store that loads events.
37637      */
37638     eventStore : 25,
37639
37640      
37641     activeDate : false,
37642     startDay : 0,
37643     autoWidth : true,
37644     monitorWindowResize : false,
37645
37646     
37647     resizeColumns : function() {
37648         var col = (this.view.el.getWidth() / 7) - 3;
37649         // loop through cols, and setWidth
37650         for(var i =0 ; i < 7 ; i++){
37651             this.cm.setColumnWidth(i, col);
37652         }
37653     },
37654      setDate :function(date) {
37655         
37656         Roo.log('setDate?');
37657         
37658         this.resizeColumns();
37659         var vd = this.activeDate;
37660         this.activeDate = date;
37661 //        if(vd && this.el){
37662 //            var t = date.getTime();
37663 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37664 //                Roo.log('using add remove');
37665 //                
37666 //                this.fireEvent('monthchange', this, date);
37667 //                
37668 //                this.cells.removeClass("fc-state-highlight");
37669 //                this.cells.each(function(c){
37670 //                   if(c.dateValue == t){
37671 //                       c.addClass("fc-state-highlight");
37672 //                       setTimeout(function(){
37673 //                            try{c.dom.firstChild.focus();}catch(e){}
37674 //                       }, 50);
37675 //                       return false;
37676 //                   }
37677 //                   return true;
37678 //                });
37679 //                return;
37680 //            }
37681 //        }
37682         
37683         var days = date.getDaysInMonth();
37684         
37685         var firstOfMonth = date.getFirstDateOfMonth();
37686         var startingPos = firstOfMonth.getDay()-this.startDay;
37687         
37688         if(startingPos < this.startDay){
37689             startingPos += 7;
37690         }
37691         
37692         var pm = date.add(Date.MONTH, -1);
37693         var prevStart = pm.getDaysInMonth()-startingPos;
37694 //        
37695         
37696         
37697         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37698         
37699         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37700         //this.cells.addClassOnOver('fc-state-hover');
37701         
37702         var cells = this.cells.elements;
37703         var textEls = this.textNodes;
37704         
37705         //Roo.each(cells, function(cell){
37706         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37707         //});
37708         
37709         days += startingPos;
37710
37711         // convert everything to numbers so it's fast
37712         var day = 86400000;
37713         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37714         //Roo.log(d);
37715         //Roo.log(pm);
37716         //Roo.log(prevStart);
37717         
37718         var today = new Date().clearTime().getTime();
37719         var sel = date.clearTime().getTime();
37720         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37721         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37722         var ddMatch = this.disabledDatesRE;
37723         var ddText = this.disabledDatesText;
37724         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37725         var ddaysText = this.disabledDaysText;
37726         var format = this.format;
37727         
37728         var setCellClass = function(cal, cell){
37729             
37730             //Roo.log('set Cell Class');
37731             cell.title = "";
37732             var t = d.getTime();
37733             
37734             //Roo.log(d);
37735             
37736             
37737             cell.dateValue = t;
37738             if(t == today){
37739                 cell.className += " fc-today";
37740                 cell.className += " fc-state-highlight";
37741                 cell.title = cal.todayText;
37742             }
37743             if(t == sel){
37744                 // disable highlight in other month..
37745                 cell.className += " fc-state-highlight";
37746                 
37747             }
37748             // disabling
37749             if(t < min) {
37750                 //cell.className = " fc-state-disabled";
37751                 cell.title = cal.minText;
37752                 return;
37753             }
37754             if(t > max) {
37755                 //cell.className = " fc-state-disabled";
37756                 cell.title = cal.maxText;
37757                 return;
37758             }
37759             if(ddays){
37760                 if(ddays.indexOf(d.getDay()) != -1){
37761                     // cell.title = ddaysText;
37762                    // cell.className = " fc-state-disabled";
37763                 }
37764             }
37765             if(ddMatch && format){
37766                 var fvalue = d.dateFormat(format);
37767                 if(ddMatch.test(fvalue)){
37768                     cell.title = ddText.replace("%0", fvalue);
37769                    cell.className = " fc-state-disabled";
37770                 }
37771             }
37772             
37773             if (!cell.initialClassName) {
37774                 cell.initialClassName = cell.dom.className;
37775             }
37776             
37777             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
37778         };
37779
37780         var i = 0;
37781         
37782         for(; i < startingPos; i++) {
37783             cells[i].dayName =  (++prevStart);
37784             Roo.log(textEls[i]);
37785             d.setDate(d.getDate()+1);
37786             
37787             //cells[i].className = "fc-past fc-other-month";
37788             setCellClass(this, cells[i]);
37789         }
37790         
37791         var intDay = 0;
37792         
37793         for(; i < days; i++){
37794             intDay = i - startingPos + 1;
37795             cells[i].dayName =  (intDay);
37796             d.setDate(d.getDate()+1);
37797             
37798             cells[i].className = ''; // "x-date-active";
37799             setCellClass(this, cells[i]);
37800         }
37801         var extraDays = 0;
37802         
37803         for(; i < 42; i++) {
37804             //textEls[i].innerHTML = (++extraDays);
37805             
37806             d.setDate(d.getDate()+1);
37807             cells[i].dayName = (++extraDays);
37808             cells[i].className = "fc-future fc-other-month";
37809             setCellClass(this, cells[i]);
37810         }
37811         
37812         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37813         
37814         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37815         
37816         // this will cause all the cells to mis
37817         var rows= [];
37818         var i =0;
37819         for (var r = 0;r < 6;r++) {
37820             for (var c =0;c < 7;c++) {
37821                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37822             }    
37823         }
37824         
37825         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37826         for(i=0;i<cells.length;i++) {
37827             
37828             this.cells.elements[i].dayName = cells[i].dayName ;
37829             this.cells.elements[i].className = cells[i].className;
37830             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37831             this.cells.elements[i].title = cells[i].title ;
37832             this.cells.elements[i].dateValue = cells[i].dateValue ;
37833         }
37834         
37835         
37836         
37837         
37838         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37839         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37840         
37841         ////if(totalRows != 6){
37842             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37843            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37844        // }
37845         
37846         this.fireEvent('monthchange', this, date);
37847         
37848         
37849     },
37850  /**
37851      * Returns the grid's SelectionModel.
37852      * @return {SelectionModel}
37853      */
37854     getSelectionModel : function(){
37855         if(!this.selModel){
37856             this.selModel = new Roo.grid.CellSelectionModel();
37857         }
37858         return this.selModel;
37859     },
37860
37861     load: function() {
37862         this.eventStore.load()
37863         
37864         
37865         
37866     },
37867     
37868     findCell : function(dt) {
37869         dt = dt.clearTime().getTime();
37870         var ret = false;
37871         this.cells.each(function(c){
37872             //Roo.log("check " +c.dateValue + '?=' + dt);
37873             if(c.dateValue == dt){
37874                 ret = c;
37875                 return false;
37876             }
37877             return true;
37878         });
37879         
37880         return ret;
37881     },
37882     
37883     findCells : function(rec) {
37884         var s = rec.data.start_dt.clone().clearTime().getTime();
37885        // Roo.log(s);
37886         var e= rec.data.end_dt.clone().clearTime().getTime();
37887        // Roo.log(e);
37888         var ret = [];
37889         this.cells.each(function(c){
37890              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37891             
37892             if(c.dateValue > e){
37893                 return ;
37894             }
37895             if(c.dateValue < s){
37896                 return ;
37897             }
37898             ret.push(c);
37899         });
37900         
37901         return ret;    
37902     },
37903     
37904     findBestRow: function(cells)
37905     {
37906         var ret = 0;
37907         
37908         for (var i =0 ; i < cells.length;i++) {
37909             ret  = Math.max(cells[i].rows || 0,ret);
37910         }
37911         return ret;
37912         
37913     },
37914     
37915     
37916     addItem : function(rec)
37917     {
37918         // look for vertical location slot in
37919         var cells = this.findCells(rec);
37920         
37921         rec.row = this.findBestRow(cells);
37922         
37923         // work out the location.
37924         
37925         var crow = false;
37926         var rows = [];
37927         for(var i =0; i < cells.length; i++) {
37928             if (!crow) {
37929                 crow = {
37930                     start : cells[i],
37931                     end :  cells[i]
37932                 };
37933                 continue;
37934             }
37935             if (crow.start.getY() == cells[i].getY()) {
37936                 // on same row.
37937                 crow.end = cells[i];
37938                 continue;
37939             }
37940             // different row.
37941             rows.push(crow);
37942             crow = {
37943                 start: cells[i],
37944                 end : cells[i]
37945             };
37946             
37947         }
37948         
37949         rows.push(crow);
37950         rec.els = [];
37951         rec.rows = rows;
37952         rec.cells = cells;
37953         for (var i = 0; i < cells.length;i++) {
37954             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
37955             
37956         }
37957         
37958         
37959     },
37960     
37961     clearEvents: function() {
37962         
37963         if (!this.eventStore.getCount()) {
37964             return;
37965         }
37966         // reset number of rows in cells.
37967         Roo.each(this.cells.elements, function(c){
37968             c.rows = 0;
37969         });
37970         
37971         this.eventStore.each(function(e) {
37972             this.clearEvent(e);
37973         },this);
37974         
37975     },
37976     
37977     clearEvent : function(ev)
37978     {
37979         if (ev.els) {
37980             Roo.each(ev.els, function(el) {
37981                 el.un('mouseenter' ,this.onEventEnter, this);
37982                 el.un('mouseleave' ,this.onEventLeave, this);
37983                 el.remove();
37984             },this);
37985             ev.els = [];
37986         }
37987     },
37988     
37989     
37990     renderEvent : function(ev,ctr) {
37991         if (!ctr) {
37992              ctr = this.view.el.select('.fc-event-container',true).first();
37993         }
37994         
37995          
37996         this.clearEvent(ev);
37997             //code
37998        
37999         
38000         
38001         ev.els = [];
38002         var cells = ev.cells;
38003         var rows = ev.rows;
38004         this.fireEvent('eventrender', this, ev);
38005         
38006         for(var i =0; i < rows.length; i++) {
38007             
38008             cls = '';
38009             if (i == 0) {
38010                 cls += ' fc-event-start';
38011             }
38012             if ((i+1) == rows.length) {
38013                 cls += ' fc-event-end';
38014             }
38015             
38016             //Roo.log(ev.data);
38017             // how many rows should it span..
38018             var cg = this.eventTmpl.append(ctr,Roo.apply({
38019                 fccls : cls
38020                 
38021             }, ev.data) , true);
38022             
38023             
38024             cg.on('mouseenter' ,this.onEventEnter, this, ev);
38025             cg.on('mouseleave' ,this.onEventLeave, this, ev);
38026             cg.on('click', this.onEventClick, this, ev);
38027             
38028             ev.els.push(cg);
38029             
38030             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
38031             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
38032             //Roo.log(cg);
38033              
38034             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
38035             cg.setWidth(ebox.right - sbox.x -2);
38036         }
38037     },
38038     
38039     renderEvents: function()
38040     {   
38041         // first make sure there is enough space..
38042         
38043         if (!this.eventTmpl) {
38044             this.eventTmpl = new Roo.Template(
38045                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
38046                     '<div class="fc-event-inner">' +
38047                         '<span class="fc-event-time">{time}</span>' +
38048                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
38049                     '</div>' +
38050                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
38051                 '</div>'
38052             );
38053                 
38054         }
38055                
38056         
38057         
38058         this.cells.each(function(c) {
38059             //Roo.log(c.select('.fc-day-content div',true).first());
38060             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
38061         });
38062         
38063         var ctr = this.view.el.select('.fc-event-container',true).first();
38064         
38065         var cls;
38066         this.eventStore.each(function(ev){
38067             
38068             this.renderEvent(ev);
38069              
38070              
38071         }, this);
38072         this.view.layout();
38073         
38074     },
38075     
38076     onEventEnter: function (e, el,event,d) {
38077         this.fireEvent('evententer', this, el, event);
38078     },
38079     
38080     onEventLeave: function (e, el,event,d) {
38081         this.fireEvent('eventleave', this, el, event);
38082     },
38083     
38084     onEventClick: function (e, el,event,d) {
38085         this.fireEvent('eventclick', this, el, event);
38086     },
38087     
38088     onMonthChange: function () {
38089         this.store.load();
38090     },
38091     
38092     onLoad: function () {
38093         
38094         //Roo.log('calendar onload');
38095 //         
38096         if(this.eventStore.getCount() > 0){
38097             
38098            
38099             
38100             this.eventStore.each(function(d){
38101                 
38102                 
38103                 // FIXME..
38104                 var add =   d.data;
38105                 if (typeof(add.end_dt) == 'undefined')  {
38106                     Roo.log("Missing End time in calendar data: ");
38107                     Roo.log(d);
38108                     return;
38109                 }
38110                 if (typeof(add.start_dt) == 'undefined')  {
38111                     Roo.log("Missing Start time in calendar data: ");
38112                     Roo.log(d);
38113                     return;
38114                 }
38115                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
38116                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
38117                 add.id = add.id || d.id;
38118                 add.title = add.title || '??';
38119                 
38120                 this.addItem(d);
38121                 
38122              
38123             },this);
38124         }
38125         
38126         this.renderEvents();
38127     }
38128     
38129
38130 });
38131 /*
38132  grid : {
38133                 xtype: 'Grid',
38134                 xns: Roo.grid,
38135                 listeners : {
38136                     render : function ()
38137                     {
38138                         _this.grid = this;
38139                         
38140                         if (!this.view.el.hasClass('course-timesheet')) {
38141                             this.view.el.addClass('course-timesheet');
38142                         }
38143                         if (this.tsStyle) {
38144                             this.ds.load({});
38145                             return; 
38146                         }
38147                         Roo.log('width');
38148                         Roo.log(_this.grid.view.el.getWidth());
38149                         
38150                         
38151                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
38152                             '.course-timesheet .x-grid-row' : {
38153                                 height: '80px'
38154                             },
38155                             '.x-grid-row td' : {
38156                                 'vertical-align' : 0
38157                             },
38158                             '.course-edit-link' : {
38159                                 'color' : 'blue',
38160                                 'text-overflow' : 'ellipsis',
38161                                 'overflow' : 'hidden',
38162                                 'white-space' : 'nowrap',
38163                                 'cursor' : 'pointer'
38164                             },
38165                             '.sub-link' : {
38166                                 'color' : 'green'
38167                             },
38168                             '.de-act-sup-link' : {
38169                                 'color' : 'purple',
38170                                 'text-decoration' : 'line-through'
38171                             },
38172                             '.de-act-link' : {
38173                                 'color' : 'red',
38174                                 'text-decoration' : 'line-through'
38175                             },
38176                             '.course-timesheet .course-highlight' : {
38177                                 'border-top-style': 'dashed !important',
38178                                 'border-bottom-bottom': 'dashed !important'
38179                             },
38180                             '.course-timesheet .course-item' : {
38181                                 'font-family'   : 'tahoma, arial, helvetica',
38182                                 'font-size'     : '11px',
38183                                 'overflow'      : 'hidden',
38184                                 'padding-left'  : '10px',
38185                                 'padding-right' : '10px',
38186                                 'padding-top' : '10px' 
38187                             }
38188                             
38189                         }, Roo.id());
38190                                 this.ds.load({});
38191                     }
38192                 },
38193                 autoWidth : true,
38194                 monitorWindowResize : false,
38195                 cellrenderer : function(v,x,r)
38196                 {
38197                     return v;
38198                 },
38199                 sm : {
38200                     xtype: 'CellSelectionModel',
38201                     xns: Roo.grid
38202                 },
38203                 dataSource : {
38204                     xtype: 'Store',
38205                     xns: Roo.data,
38206                     listeners : {
38207                         beforeload : function (_self, options)
38208                         {
38209                             options.params = options.params || {};
38210                             options.params._month = _this.monthField.getValue();
38211                             options.params.limit = 9999;
38212                             options.params['sort'] = 'when_dt';    
38213                             options.params['dir'] = 'ASC';    
38214                             this.proxy.loadResponse = this.loadResponse;
38215                             Roo.log("load?");
38216                             //this.addColumns();
38217                         },
38218                         load : function (_self, records, options)
38219                         {
38220                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
38221                                 // if you click on the translation.. you can edit it...
38222                                 var el = Roo.get(this);
38223                                 var id = el.dom.getAttribute('data-id');
38224                                 var d = el.dom.getAttribute('data-date');
38225                                 var t = el.dom.getAttribute('data-time');
38226                                 //var id = this.child('span').dom.textContent;
38227                                 
38228                                 //Roo.log(this);
38229                                 Pman.Dialog.CourseCalendar.show({
38230                                     id : id,
38231                                     when_d : d,
38232                                     when_t : t,
38233                                     productitem_active : id ? 1 : 0
38234                                 }, function() {
38235                                     _this.grid.ds.load({});
38236                                 });
38237                            
38238                            });
38239                            
38240                            _this.panel.fireEvent('resize', [ '', '' ]);
38241                         }
38242                     },
38243                     loadResponse : function(o, success, response){
38244                             // this is overridden on before load..
38245                             
38246                             Roo.log("our code?");       
38247                             //Roo.log(success);
38248                             //Roo.log(response)
38249                             delete this.activeRequest;
38250                             if(!success){
38251                                 this.fireEvent("loadexception", this, o, response);
38252                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38253                                 return;
38254                             }
38255                             var result;
38256                             try {
38257                                 result = o.reader.read(response);
38258                             }catch(e){
38259                                 Roo.log("load exception?");
38260                                 this.fireEvent("loadexception", this, o, response, e);
38261                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38262                                 return;
38263                             }
38264                             Roo.log("ready...");        
38265                             // loop through result.records;
38266                             // and set this.tdate[date] = [] << array of records..
38267                             _this.tdata  = {};
38268                             Roo.each(result.records, function(r){
38269                                 //Roo.log(r.data);
38270                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
38271                                     _this.tdata[r.data.when_dt.format('j')] = [];
38272                                 }
38273                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
38274                             });
38275                             
38276                             //Roo.log(_this.tdata);
38277                             
38278                             result.records = [];
38279                             result.totalRecords = 6;
38280                     
38281                             // let's generate some duumy records for the rows.
38282                             //var st = _this.dateField.getValue();
38283                             
38284                             // work out monday..
38285                             //st = st.add(Date.DAY, -1 * st.format('w'));
38286                             
38287                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38288                             
38289                             var firstOfMonth = date.getFirstDayOfMonth();
38290                             var days = date.getDaysInMonth();
38291                             var d = 1;
38292                             var firstAdded = false;
38293                             for (var i = 0; i < result.totalRecords ; i++) {
38294                                 //var d= st.add(Date.DAY, i);
38295                                 var row = {};
38296                                 var added = 0;
38297                                 for(var w = 0 ; w < 7 ; w++){
38298                                     if(!firstAdded && firstOfMonth != w){
38299                                         continue;
38300                                     }
38301                                     if(d > days){
38302                                         continue;
38303                                     }
38304                                     firstAdded = true;
38305                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
38306                                     row['weekday'+w] = String.format(
38307                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
38308                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
38309                                                     d,
38310                                                     date.format('Y-m-')+dd
38311                                                 );
38312                                     added++;
38313                                     if(typeof(_this.tdata[d]) != 'undefined'){
38314                                         Roo.each(_this.tdata[d], function(r){
38315                                             var is_sub = '';
38316                                             var deactive = '';
38317                                             var id = r.id;
38318                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
38319                                             if(r.parent_id*1>0){
38320                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
38321                                                 id = r.parent_id;
38322                                             }
38323                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
38324                                                 deactive = 'de-act-link';
38325                                             }
38326                                             
38327                                             row['weekday'+w] += String.format(
38328                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
38329                                                     id, //0
38330                                                     r.product_id_name, //1
38331                                                     r.when_dt.format('h:ia'), //2
38332                                                     is_sub, //3
38333                                                     deactive, //4
38334                                                     desc // 5
38335                                             );
38336                                         });
38337                                     }
38338                                     d++;
38339                                 }
38340                                 
38341                                 // only do this if something added..
38342                                 if(added > 0){ 
38343                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
38344                                 }
38345                                 
38346                                 
38347                                 // push it twice. (second one with an hour..
38348                                 
38349                             }
38350                             //Roo.log(result);
38351                             this.fireEvent("load", this, o, o.request.arg);
38352                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
38353                         },
38354                     sortInfo : {field: 'when_dt', direction : 'ASC' },
38355                     proxy : {
38356                         xtype: 'HttpProxy',
38357                         xns: Roo.data,
38358                         method : 'GET',
38359                         url : baseURL + '/Roo/Shop_course.php'
38360                     },
38361                     reader : {
38362                         xtype: 'JsonReader',
38363                         xns: Roo.data,
38364                         id : 'id',
38365                         fields : [
38366                             {
38367                                 'name': 'id',
38368                                 'type': 'int'
38369                             },
38370                             {
38371                                 'name': 'when_dt',
38372                                 'type': 'string'
38373                             },
38374                             {
38375                                 'name': 'end_dt',
38376                                 'type': 'string'
38377                             },
38378                             {
38379                                 'name': 'parent_id',
38380                                 'type': 'int'
38381                             },
38382                             {
38383                                 'name': 'product_id',
38384                                 'type': 'int'
38385                             },
38386                             {
38387                                 'name': 'productitem_id',
38388                                 'type': 'int'
38389                             },
38390                             {
38391                                 'name': 'guid',
38392                                 'type': 'int'
38393                             }
38394                         ]
38395                     }
38396                 },
38397                 toolbar : {
38398                     xtype: 'Toolbar',
38399                     xns: Roo,
38400                     items : [
38401                         {
38402                             xtype: 'Button',
38403                             xns: Roo.Toolbar,
38404                             listeners : {
38405                                 click : function (_self, e)
38406                                 {
38407                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38408                                     sd.setMonth(sd.getMonth()-1);
38409                                     _this.monthField.setValue(sd.format('Y-m-d'));
38410                                     _this.grid.ds.load({});
38411                                 }
38412                             },
38413                             text : "Back"
38414                         },
38415                         {
38416                             xtype: 'Separator',
38417                             xns: Roo.Toolbar
38418                         },
38419                         {
38420                             xtype: 'MonthField',
38421                             xns: Roo.form,
38422                             listeners : {
38423                                 render : function (_self)
38424                                 {
38425                                     _this.monthField = _self;
38426                                    // _this.monthField.set  today
38427                                 },
38428                                 select : function (combo, date)
38429                                 {
38430                                     _this.grid.ds.load({});
38431                                 }
38432                             },
38433                             value : (function() { return new Date(); })()
38434                         },
38435                         {
38436                             xtype: 'Separator',
38437                             xns: Roo.Toolbar
38438                         },
38439                         {
38440                             xtype: 'TextItem',
38441                             xns: Roo.Toolbar,
38442                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
38443                         },
38444                         {
38445                             xtype: 'Fill',
38446                             xns: Roo.Toolbar
38447                         },
38448                         {
38449                             xtype: 'Button',
38450                             xns: Roo.Toolbar,
38451                             listeners : {
38452                                 click : function (_self, e)
38453                                 {
38454                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38455                                     sd.setMonth(sd.getMonth()+1);
38456                                     _this.monthField.setValue(sd.format('Y-m-d'));
38457                                     _this.grid.ds.load({});
38458                                 }
38459                             },
38460                             text : "Next"
38461                         }
38462                     ]
38463                 },
38464                  
38465             }
38466         };
38467         
38468         *//*
38469  * Based on:
38470  * Ext JS Library 1.1.1
38471  * Copyright(c) 2006-2007, Ext JS, LLC.
38472  *
38473  * Originally Released Under LGPL - original licence link has changed is not relivant.
38474  *
38475  * Fork - LGPL
38476  * <script type="text/javascript">
38477  */
38478  
38479 /**
38480  * @class Roo.LoadMask
38481  * A simple utility class for generically masking elements while loading data.  If the element being masked has
38482  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38483  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
38484  * element's UpdateManager load indicator and will be destroyed after the initial load.
38485  * @constructor
38486  * Create a new LoadMask
38487  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38488  * @param {Object} config The config object
38489  */
38490 Roo.LoadMask = function(el, config){
38491     this.el = Roo.get(el);
38492     Roo.apply(this, config);
38493     if(this.store){
38494         this.store.on('beforeload', this.onBeforeLoad, this);
38495         this.store.on('load', this.onLoad, this);
38496         this.store.on('loadexception', this.onLoadException, this);
38497         this.removeMask = false;
38498     }else{
38499         var um = this.el.getUpdateManager();
38500         um.showLoadIndicator = false; // disable the default indicator
38501         um.on('beforeupdate', this.onBeforeLoad, this);
38502         um.on('update', this.onLoad, this);
38503         um.on('failure', this.onLoad, this);
38504         this.removeMask = true;
38505     }
38506 };
38507
38508 Roo.LoadMask.prototype = {
38509     /**
38510      * @cfg {Boolean} removeMask
38511      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38512      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
38513      */
38514     removeMask : false,
38515     /**
38516      * @cfg {String} msg
38517      * The text to display in a centered loading message box (defaults to 'Loading...')
38518      */
38519     msg : 'Loading...',
38520     /**
38521      * @cfg {String} msgCls
38522      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38523      */
38524     msgCls : 'x-mask-loading',
38525
38526     /**
38527      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38528      * @type Boolean
38529      */
38530     disabled: false,
38531
38532     /**
38533      * Disables the mask to prevent it from being displayed
38534      */
38535     disable : function(){
38536        this.disabled = true;
38537     },
38538
38539     /**
38540      * Enables the mask so that it can be displayed
38541      */
38542     enable : function(){
38543         this.disabled = false;
38544     },
38545     
38546     onLoadException : function()
38547     {
38548         Roo.log(arguments);
38549         
38550         if (typeof(arguments[3]) != 'undefined') {
38551             Roo.MessageBox.alert("Error loading",arguments[3]);
38552         } 
38553         /*
38554         try {
38555             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38556                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38557             }   
38558         } catch(e) {
38559             
38560         }
38561         */
38562     
38563         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38564     },
38565     // private
38566     onLoad : function()
38567     {
38568         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38569     },
38570
38571     // private
38572     onBeforeLoad : function(){
38573         if(!this.disabled){
38574             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
38575         }
38576     },
38577
38578     // private
38579     destroy : function(){
38580         if(this.store){
38581             this.store.un('beforeload', this.onBeforeLoad, this);
38582             this.store.un('load', this.onLoad, this);
38583             this.store.un('loadexception', this.onLoadException, this);
38584         }else{
38585             var um = this.el.getUpdateManager();
38586             um.un('beforeupdate', this.onBeforeLoad, this);
38587             um.un('update', this.onLoad, this);
38588             um.un('failure', this.onLoad, this);
38589         }
38590     }
38591 };/*
38592  * Based on:
38593  * Ext JS Library 1.1.1
38594  * Copyright(c) 2006-2007, Ext JS, LLC.
38595  *
38596  * Originally Released Under LGPL - original licence link has changed is not relivant.
38597  *
38598  * Fork - LGPL
38599  * <script type="text/javascript">
38600  */
38601
38602
38603 /**
38604  * @class Roo.XTemplate
38605  * @extends Roo.Template
38606  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38607 <pre><code>
38608 var t = new Roo.XTemplate(
38609         '&lt;select name="{name}"&gt;',
38610                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
38611         '&lt;/select&gt;'
38612 );
38613  
38614 // then append, applying the master template values
38615  </code></pre>
38616  *
38617  * Supported features:
38618  *
38619  *  Tags:
38620
38621 <pre><code>
38622       {a_variable} - output encoded.
38623       {a_variable.format:("Y-m-d")} - call a method on the variable
38624       {a_variable:raw} - unencoded output
38625       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38626       {a_variable:this.method_on_template(...)} - call a method on the template object.
38627  
38628 </code></pre>
38629  *  The tpl tag:
38630 <pre><code>
38631         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
38632         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
38633         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
38634         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
38635   
38636         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
38637         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
38638 </code></pre>
38639  *      
38640  */
38641 Roo.XTemplate = function()
38642 {
38643     Roo.XTemplate.superclass.constructor.apply(this, arguments);
38644     if (this.html) {
38645         this.compile();
38646     }
38647 };
38648
38649
38650 Roo.extend(Roo.XTemplate, Roo.Template, {
38651
38652     /**
38653      * The various sub templates
38654      */
38655     tpls : false,
38656     /**
38657      *
38658      * basic tag replacing syntax
38659      * WORD:WORD()
38660      *
38661      * // you can fake an object call by doing this
38662      *  x.t:(test,tesT) 
38663      * 
38664      */
38665     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38666
38667     /**
38668      * compile the template
38669      *
38670      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38671      *
38672      */
38673     compile: function()
38674     {
38675         var s = this.html;
38676      
38677         s = ['<tpl>', s, '</tpl>'].join('');
38678     
38679         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38680             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38681             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38682             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38683             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38684             m,
38685             id     = 0,
38686             tpls   = [];
38687     
38688         while(true == !!(m = s.match(re))){
38689             var forMatch   = m[0].match(nameRe),
38690                 ifMatch   = m[0].match(ifRe),
38691                 execMatch   = m[0].match(execRe),
38692                 namedMatch   = m[0].match(namedRe),
38693                 
38694                 exp  = null, 
38695                 fn   = null,
38696                 exec = null,
38697                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38698                 
38699             if (ifMatch) {
38700                 // if - puts fn into test..
38701                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38702                 if(exp){
38703                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38704                 }
38705             }
38706             
38707             if (execMatch) {
38708                 // exec - calls a function... returns empty if true is  returned.
38709                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38710                 if(exp){
38711                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38712                 }
38713             }
38714             
38715             
38716             if (name) {
38717                 // for = 
38718                 switch(name){
38719                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38720                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38721                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38722                 }
38723             }
38724             var uid = namedMatch ? namedMatch[1] : id;
38725             
38726             
38727             tpls.push({
38728                 id:     namedMatch ? namedMatch[1] : id,
38729                 target: name,
38730                 exec:   exec,
38731                 test:   fn,
38732                 body:   m[1] || ''
38733             });
38734             if (namedMatch) {
38735                 s = s.replace(m[0], '');
38736             } else { 
38737                 s = s.replace(m[0], '{xtpl'+ id + '}');
38738             }
38739             ++id;
38740         }
38741         this.tpls = [];
38742         for(var i = tpls.length-1; i >= 0; --i){
38743             this.compileTpl(tpls[i]);
38744             this.tpls[tpls[i].id] = tpls[i];
38745         }
38746         this.master = tpls[tpls.length-1];
38747         return this;
38748     },
38749     /**
38750      * same as applyTemplate, except it's done to one of the subTemplates
38751      * when using named templates, you can do:
38752      *
38753      * var str = pl.applySubTemplate('your-name', values);
38754      *
38755      * 
38756      * @param {Number} id of the template
38757      * @param {Object} values to apply to template
38758      * @param {Object} parent (normaly the instance of this object)
38759      */
38760     applySubTemplate : function(id, values, parent)
38761     {
38762         
38763         
38764         var t = this.tpls[id];
38765         
38766         
38767         try { 
38768             if(t.test && !t.test.call(this, values, parent)){
38769                 return '';
38770             }
38771         } catch(e) {
38772             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38773             Roo.log(e.toString());
38774             Roo.log(t.test);
38775             return ''
38776         }
38777         try { 
38778             
38779             if(t.exec && t.exec.call(this, values, parent)){
38780                 return '';
38781             }
38782         } catch(e) {
38783             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38784             Roo.log(e.toString());
38785             Roo.log(t.exec);
38786             return ''
38787         }
38788         try {
38789             var vs = t.target ? t.target.call(this, values, parent) : values;
38790             parent = t.target ? values : parent;
38791             if(t.target && vs instanceof Array){
38792                 var buf = [];
38793                 for(var i = 0, len = vs.length; i < len; i++){
38794                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38795                 }
38796                 return buf.join('');
38797             }
38798             return t.compiled.call(this, vs, parent);
38799         } catch (e) {
38800             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38801             Roo.log(e.toString());
38802             Roo.log(t.compiled);
38803             return '';
38804         }
38805     },
38806
38807     compileTpl : function(tpl)
38808     {
38809         var fm = Roo.util.Format;
38810         var useF = this.disableFormats !== true;
38811         var sep = Roo.isGecko ? "+" : ",";
38812         var undef = function(str) {
38813             Roo.log("Property not found :"  + str);
38814             return '';
38815         };
38816         
38817         var fn = function(m, name, format, args)
38818         {
38819             //Roo.log(arguments);
38820             args = args ? args.replace(/\\'/g,"'") : args;
38821             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38822             if (typeof(format) == 'undefined') {
38823                 format= 'htmlEncode';
38824             }
38825             if (format == 'raw' ) {
38826                 format = false;
38827             }
38828             
38829             if(name.substr(0, 4) == 'xtpl'){
38830                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38831             }
38832             
38833             // build an array of options to determine if value is undefined..
38834             
38835             // basically get 'xxxx.yyyy' then do
38836             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38837             //    (function () { Roo.log("Property not found"); return ''; })() :
38838             //    ......
38839             
38840             var udef_ar = [];
38841             var lookfor = '';
38842             Roo.each(name.split('.'), function(st) {
38843                 lookfor += (lookfor.length ? '.': '') + st;
38844                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38845             });
38846             
38847             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38848             
38849             
38850             if(format && useF){
38851                 
38852                 args = args ? ',' + args : "";
38853                  
38854                 if(format.substr(0, 5) != "this."){
38855                     format = "fm." + format + '(';
38856                 }else{
38857                     format = 'this.call("'+ format.substr(5) + '", ';
38858                     args = ", values";
38859                 }
38860                 
38861                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38862             }
38863              
38864             if (args.length) {
38865                 // called with xxyx.yuu:(test,test)
38866                 // change to ()
38867                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38868             }
38869             // raw.. - :raw modifier..
38870             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38871             
38872         };
38873         var body;
38874         // branched to use + in gecko and [].join() in others
38875         if(Roo.isGecko){
38876             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38877                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38878                     "';};};";
38879         }else{
38880             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38881             body.push(tpl.body.replace(/(\r\n|\n)/g,
38882                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38883             body.push("'].join('');};};");
38884             body = body.join('');
38885         }
38886         
38887         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38888        
38889         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38890         eval(body);
38891         
38892         return this;
38893     },
38894
38895     applyTemplate : function(values){
38896         return this.master.compiled.call(this, values, {});
38897         //var s = this.subs;
38898     },
38899
38900     apply : function(){
38901         return this.applyTemplate.apply(this, arguments);
38902     }
38903
38904  });
38905
38906 Roo.XTemplate.from = function(el){
38907     el = Roo.getDom(el);
38908     return new Roo.XTemplate(el.value || el.innerHTML);
38909 };