Merge branch 'master' of http://git.roojs.com/roojs1
[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 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 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      * Gets the number of cached records.
790      * <p>
791      * <em>If using paging, this may not be the total size of the dataset. If the data object
792      * used by the Reader contains the dataset size, then the getTotalCount() function returns
793      * the data set size</em>
794      */
795     getCount : function(){
796         return this.data.length || 0;
797     },
798
799     /**
800      * Gets the total number of records in the dataset as returned by the server.
801      * <p>
802      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
803      * the dataset size</em>
804      */
805     getTotalCount : function(){
806         return this.totalLength || 0;
807     },
808
809     /**
810      * Returns the sort state of the Store as an object with two properties:
811      * <pre><code>
812  field {String} The name of the field by which the Records are sorted
813  direction {String} The sort order, "ASC" or "DESC"
814      * </code></pre>
815      */
816     getSortState : function(){
817         return this.sortInfo;
818     },
819
820     // private
821     applySort : function(){
822         if(this.sortInfo && !this.remoteSort){
823             var s = this.sortInfo, f = s.field;
824             var st = this.fields.get(f).sortType;
825             var fn = function(r1, r2){
826                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
827                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
828             };
829             this.data.sort(s.direction, fn);
830             if(this.snapshot && this.snapshot != this.data){
831                 this.snapshot.sort(s.direction, fn);
832             }
833         }
834     },
835
836     /**
837      * Sets the default sort column and order to be used by the next load operation.
838      * @param {String} fieldName The name of the field to sort by.
839      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
840      */
841     setDefaultSort : function(field, dir){
842         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
843     },
844
845     /**
846      * Sort the Records.
847      * If remote sorting is used, the sort is performed on the server, and the cache is
848      * reloaded. If local sorting is used, the cache is sorted internally.
849      * @param {String} fieldName The name of the field to sort by.
850      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
851      */
852     sort : function(fieldName, dir){
853         var f = this.fields.get(fieldName);
854         if(!dir){
855             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
856             
857             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
858                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
859             }else{
860                 dir = f.sortDir;
861             }
862         }
863         this.sortToggle[f.name] = dir;
864         this.sortInfo = {field: f.name, direction: dir};
865         if(!this.remoteSort){
866             this.applySort();
867             this.fireEvent("datachanged", this);
868         }else{
869             this.load(this.lastOptions);
870         }
871     },
872
873     /**
874      * Calls the specified function for each of the Records in the cache.
875      * @param {Function} fn The function to call. The Record is passed as the first parameter.
876      * Returning <em>false</em> aborts and exits the iteration.
877      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
878      */
879     each : function(fn, scope){
880         this.data.each(fn, scope);
881     },
882
883     /**
884      * Gets all records modified since the last commit.  Modified records are persisted across load operations
885      * (e.g., during paging).
886      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
887      */
888     getModifiedRecords : function(){
889         return this.modified;
890     },
891
892     // private
893     createFilterFn : function(property, value, anyMatch){
894         if(!value.exec){ // not a regex
895             value = String(value);
896             if(value.length == 0){
897                 return false;
898             }
899             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
900         }
901         return function(r){
902             return value.test(r.data[property]);
903         };
904     },
905
906     /**
907      * Sums the value of <i>property</i> for each record between start and end and returns the result.
908      * @param {String} property A field on your records
909      * @param {Number} start The record index to start at (defaults to 0)
910      * @param {Number} end The last record index to include (defaults to length - 1)
911      * @return {Number} The sum
912      */
913     sum : function(property, start, end){
914         var rs = this.data.items, v = 0;
915         start = start || 0;
916         end = (end || end === 0) ? end : rs.length-1;
917
918         for(var i = start; i <= end; i++){
919             v += (rs[i].data[property] || 0);
920         }
921         return v;
922     },
923
924     /**
925      * Filter the records by a specified property.
926      * @param {String} field A field on your records
927      * @param {String/RegExp} value Either a string that the field
928      * should start with or a RegExp to test against the field
929      * @param {Boolean} anyMatch True to match any part not just the beginning
930      */
931     filter : function(property, value, anyMatch){
932         var fn = this.createFilterFn(property, value, anyMatch);
933         return fn ? this.filterBy(fn) : this.clearFilter();
934     },
935
936     /**
937      * Filter by a function. The specified function will be called with each
938      * record in this data source. If the function returns true the record is included,
939      * otherwise it is filtered.
940      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
941      * @param {Object} scope (optional) The scope of the function (defaults to this)
942      */
943     filterBy : function(fn, scope){
944         this.snapshot = this.snapshot || this.data;
945         this.data = this.queryBy(fn, scope||this);
946         this.fireEvent("datachanged", this);
947     },
948
949     /**
950      * Query the records by a specified property.
951      * @param {String} field A field on your records
952      * @param {String/RegExp} value Either a string that the field
953      * should start with or a RegExp to test against the field
954      * @param {Boolean} anyMatch True to match any part not just the beginning
955      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
956      */
957     query : function(property, value, anyMatch){
958         var fn = this.createFilterFn(property, value, anyMatch);
959         return fn ? this.queryBy(fn) : this.data.clone();
960     },
961
962     /**
963      * Query by a function. The specified function will be called with each
964      * record in this data source. If the function returns true the record is included
965      * in the results.
966      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
967      * @param {Object} scope (optional) The scope of the function (defaults to this)
968       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
969      **/
970     queryBy : function(fn, scope){
971         var data = this.snapshot || this.data;
972         return data.filterBy(fn, scope||this);
973     },
974
975     /**
976      * Collects unique values for a particular dataIndex from this store.
977      * @param {String} dataIndex The property to collect
978      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
979      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
980      * @return {Array} An array of the unique values
981      **/
982     collect : function(dataIndex, allowNull, bypassFilter){
983         var d = (bypassFilter === true && this.snapshot) ?
984                 this.snapshot.items : this.data.items;
985         var v, sv, r = [], l = {};
986         for(var i = 0, len = d.length; i < len; i++){
987             v = d[i].data[dataIndex];
988             sv = String(v);
989             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
990                 l[sv] = true;
991                 r[r.length] = v;
992             }
993         }
994         return r;
995     },
996
997     /**
998      * Revert to a view of the Record cache with no filtering applied.
999      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
1000      */
1001     clearFilter : function(suppressEvent){
1002         if(this.snapshot && this.snapshot != this.data){
1003             this.data = this.snapshot;
1004             delete this.snapshot;
1005             if(suppressEvent !== true){
1006                 this.fireEvent("datachanged", this);
1007             }
1008         }
1009     },
1010
1011     // private
1012     afterEdit : function(record){
1013         if(this.modified.indexOf(record) == -1){
1014             this.modified.push(record);
1015         }
1016         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1017     },
1018     
1019     // private
1020     afterReject : function(record){
1021         this.modified.remove(record);
1022         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1023     },
1024
1025     // private
1026     afterCommit : function(record){
1027         this.modified.remove(record);
1028         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1029     },
1030
1031     /**
1032      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1033      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1034      */
1035     commitChanges : function(){
1036         var m = this.modified.slice(0);
1037         this.modified = [];
1038         for(var i = 0, len = m.length; i < len; i++){
1039             m[i].commit();
1040         }
1041     },
1042
1043     /**
1044      * Cancel outstanding changes on all changed records.
1045      */
1046     rejectChanges : function(){
1047         var m = this.modified.slice(0);
1048         this.modified = [];
1049         for(var i = 0, len = m.length; i < len; i++){
1050             m[i].reject();
1051         }
1052     },
1053
1054     onMetaChange : function(meta, rtype, o){
1055         this.recordType = rtype;
1056         this.fields = rtype.prototype.fields;
1057         delete this.snapshot;
1058         this.sortInfo = meta.sortInfo || this.sortInfo;
1059         this.modified = [];
1060         this.fireEvent('metachange', this, this.reader.meta);
1061     },
1062     
1063     moveIndex : function(data, type)
1064     {
1065         var index = this.indexOf(data);
1066         
1067         var newIndex = index + type;
1068         
1069         this.remove(data);
1070         
1071         this.insert(newIndex, data);
1072         
1073     }
1074 });/*
1075  * Based on:
1076  * Ext JS Library 1.1.1
1077  * Copyright(c) 2006-2007, Ext JS, LLC.
1078  *
1079  * Originally Released Under LGPL - original licence link has changed is not relivant.
1080  *
1081  * Fork - LGPL
1082  * <script type="text/javascript">
1083  */
1084
1085 /**
1086  * @class Roo.data.SimpleStore
1087  * @extends Roo.data.Store
1088  * Small helper class to make creating Stores from Array data easier.
1089  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1090  * @cfg {Array} fields An array of field definition objects, or field name strings.
1091  * @cfg {Array} data The multi-dimensional array of data
1092  * @constructor
1093  * @param {Object} config
1094  */
1095 Roo.data.SimpleStore = function(config){
1096     Roo.data.SimpleStore.superclass.constructor.call(this, {
1097         isLocal : true,
1098         reader: new Roo.data.ArrayReader({
1099                 id: config.id
1100             },
1101             Roo.data.Record.create(config.fields)
1102         ),
1103         proxy : new Roo.data.MemoryProxy(config.data)
1104     });
1105     this.load();
1106 };
1107 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1108  * Based on:
1109  * Ext JS Library 1.1.1
1110  * Copyright(c) 2006-2007, Ext JS, LLC.
1111  *
1112  * Originally Released Under LGPL - original licence link has changed is not relivant.
1113  *
1114  * Fork - LGPL
1115  * <script type="text/javascript">
1116  */
1117
1118 /**
1119 /**
1120  * @extends Roo.data.Store
1121  * @class Roo.data.JsonStore
1122  * Small helper class to make creating Stores for JSON data easier. <br/>
1123 <pre><code>
1124 var store = new Roo.data.JsonStore({
1125     url: 'get-images.php',
1126     root: 'images',
1127     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1128 });
1129 </code></pre>
1130  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1131  * JsonReader and HttpProxy (unless inline data is provided).</b>
1132  * @cfg {Array} fields An array of field definition objects, or field name strings.
1133  * @constructor
1134  * @param {Object} config
1135  */
1136 Roo.data.JsonStore = function(c){
1137     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1138         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1139         reader: new Roo.data.JsonReader(c, c.fields)
1140     }));
1141 };
1142 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1143  * Based on:
1144  * Ext JS Library 1.1.1
1145  * Copyright(c) 2006-2007, Ext JS, LLC.
1146  *
1147  * Originally Released Under LGPL - original licence link has changed is not relivant.
1148  *
1149  * Fork - LGPL
1150  * <script type="text/javascript">
1151  */
1152
1153  
1154 Roo.data.Field = function(config){
1155     if(typeof config == "string"){
1156         config = {name: config};
1157     }
1158     Roo.apply(this, config);
1159     
1160     if(!this.type){
1161         this.type = "auto";
1162     }
1163     
1164     var st = Roo.data.SortTypes;
1165     // named sortTypes are supported, here we look them up
1166     if(typeof this.sortType == "string"){
1167         this.sortType = st[this.sortType];
1168     }
1169     
1170     // set default sortType for strings and dates
1171     if(!this.sortType){
1172         switch(this.type){
1173             case "string":
1174                 this.sortType = st.asUCString;
1175                 break;
1176             case "date":
1177                 this.sortType = st.asDate;
1178                 break;
1179             default:
1180                 this.sortType = st.none;
1181         }
1182     }
1183
1184     // define once
1185     var stripRe = /[\$,%]/g;
1186
1187     // prebuilt conversion function for this field, instead of
1188     // switching every time we're reading a value
1189     if(!this.convert){
1190         var cv, dateFormat = this.dateFormat;
1191         switch(this.type){
1192             case "":
1193             case "auto":
1194             case undefined:
1195                 cv = function(v){ return v; };
1196                 break;
1197             case "string":
1198                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1199                 break;
1200             case "int":
1201                 cv = function(v){
1202                     return v !== undefined && v !== null && v !== '' ?
1203                            parseInt(String(v).replace(stripRe, ""), 10) : '';
1204                     };
1205                 break;
1206             case "float":
1207                 cv = function(v){
1208                     return v !== undefined && v !== null && v !== '' ?
1209                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
1210                     };
1211                 break;
1212             case "bool":
1213             case "boolean":
1214                 cv = function(v){ return v === true || v === "true" || v == 1; };
1215                 break;
1216             case "date":
1217                 cv = function(v){
1218                     if(!v){
1219                         return '';
1220                     }
1221                     if(v instanceof Date){
1222                         return v;
1223                     }
1224                     if(dateFormat){
1225                         if(dateFormat == "timestamp"){
1226                             return new Date(v*1000);
1227                         }
1228                         return Date.parseDate(v, dateFormat);
1229                     }
1230                     var parsed = Date.parse(v);
1231                     return parsed ? new Date(parsed) : null;
1232                 };
1233              break;
1234             
1235         }
1236         this.convert = cv;
1237     }
1238 };
1239
1240 Roo.data.Field.prototype = {
1241     dateFormat: null,
1242     defaultValue: "",
1243     mapping: null,
1244     sortType : null,
1245     sortDir : "ASC"
1246 };/*
1247  * Based on:
1248  * Ext JS Library 1.1.1
1249  * Copyright(c) 2006-2007, Ext JS, LLC.
1250  *
1251  * Originally Released Under LGPL - original licence link has changed is not relivant.
1252  *
1253  * Fork - LGPL
1254  * <script type="text/javascript">
1255  */
1256  
1257 // Base class for reading structured data from a data source.  This class is intended to be
1258 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1259
1260 /**
1261  * @class Roo.data.DataReader
1262  * Base class for reading structured data from a data source.  This class is intended to be
1263  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1264  */
1265
1266 Roo.data.DataReader = function(meta, recordType){
1267     
1268     this.meta = meta;
1269     
1270     this.recordType = recordType instanceof Array ? 
1271         Roo.data.Record.create(recordType) : recordType;
1272 };
1273
1274 Roo.data.DataReader.prototype = {
1275      /**
1276      * Create an empty record
1277      * @param {Object} data (optional) - overlay some values
1278      * @return {Roo.data.Record} record created.
1279      */
1280     newRow :  function(d) {
1281         var da =  {};
1282         this.recordType.prototype.fields.each(function(c) {
1283             switch( c.type) {
1284                 case 'int' : da[c.name] = 0; break;
1285                 case 'date' : da[c.name] = new Date(); break;
1286                 case 'float' : da[c.name] = 0.0; break;
1287                 case 'boolean' : da[c.name] = false; break;
1288                 default : da[c.name] = ""; break;
1289             }
1290             
1291         });
1292         return new this.recordType(Roo.apply(da, d));
1293     }
1294     
1295 };/*
1296  * Based on:
1297  * Ext JS Library 1.1.1
1298  * Copyright(c) 2006-2007, Ext JS, LLC.
1299  *
1300  * Originally Released Under LGPL - original licence link has changed is not relivant.
1301  *
1302  * Fork - LGPL
1303  * <script type="text/javascript">
1304  */
1305
1306 /**
1307  * @class Roo.data.DataProxy
1308  * @extends Roo.data.Observable
1309  * This class is an abstract base class for implementations which provide retrieval of
1310  * unformatted data objects.<br>
1311  * <p>
1312  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1313  * (of the appropriate type which knows how to parse the data object) to provide a block of
1314  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1315  * <p>
1316  * Custom implementations must implement the load method as described in
1317  * {@link Roo.data.HttpProxy#load}.
1318  */
1319 Roo.data.DataProxy = function(){
1320     this.addEvents({
1321         /**
1322          * @event beforeload
1323          * Fires before a network request is made to retrieve a data object.
1324          * @param {Object} This DataProxy object.
1325          * @param {Object} params The params parameter to the load function.
1326          */
1327         beforeload : true,
1328         /**
1329          * @event load
1330          * Fires before the load method's callback is called.
1331          * @param {Object} This DataProxy object.
1332          * @param {Object} o The data object.
1333          * @param {Object} arg The callback argument object passed to the load function.
1334          */
1335         load : true,
1336         /**
1337          * @event loadexception
1338          * Fires if an Exception occurs during data retrieval.
1339          * @param {Object} This DataProxy object.
1340          * @param {Object} o The data object.
1341          * @param {Object} arg The callback argument object passed to the load function.
1342          * @param {Object} e The Exception.
1343          */
1344         loadexception : true
1345     });
1346     Roo.data.DataProxy.superclass.constructor.call(this);
1347 };
1348
1349 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1350
1351     /**
1352      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1353      */
1354 /*
1355  * Based on:
1356  * Ext JS Library 1.1.1
1357  * Copyright(c) 2006-2007, Ext JS, LLC.
1358  *
1359  * Originally Released Under LGPL - original licence link has changed is not relivant.
1360  *
1361  * Fork - LGPL
1362  * <script type="text/javascript">
1363  */
1364 /**
1365  * @class Roo.data.MemoryProxy
1366  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1367  * to the Reader when its load method is called.
1368  * @constructor
1369  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1370  */
1371 Roo.data.MemoryProxy = function(data){
1372     if (data.data) {
1373         data = data.data;
1374     }
1375     Roo.data.MemoryProxy.superclass.constructor.call(this);
1376     this.data = data;
1377 };
1378
1379 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1380     
1381     /**
1382      * Load data from the requested source (in this case an in-memory
1383      * data object passed to the constructor), read the data object into
1384      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1385      * process that block using the passed callback.
1386      * @param {Object} params This parameter is not used by the MemoryProxy class.
1387      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1388      * object into a block of Roo.data.Records.
1389      * @param {Function} callback The function into which to pass the block of Roo.data.records.
1390      * The function must be passed <ul>
1391      * <li>The Record block object</li>
1392      * <li>The "arg" argument from the load function</li>
1393      * <li>A boolean success indicator</li>
1394      * </ul>
1395      * @param {Object} scope The scope in which to call the callback
1396      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1397      */
1398     load : function(params, reader, callback, scope, arg){
1399         params = params || {};
1400         var result;
1401         try {
1402             result = reader.readRecords(this.data);
1403         }catch(e){
1404             this.fireEvent("loadexception", this, arg, null, e);
1405             callback.call(scope, null, arg, false);
1406             return;
1407         }
1408         callback.call(scope, result, arg, true);
1409     },
1410     
1411     // private
1412     update : function(params, records){
1413         
1414     }
1415 });/*
1416  * Based on:
1417  * Ext JS Library 1.1.1
1418  * Copyright(c) 2006-2007, Ext JS, LLC.
1419  *
1420  * Originally Released Under LGPL - original licence link has changed is not relivant.
1421  *
1422  * Fork - LGPL
1423  * <script type="text/javascript">
1424  */
1425 /**
1426  * @class Roo.data.HttpProxy
1427  * @extends Roo.data.DataProxy
1428  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1429  * configured to reference a certain URL.<br><br>
1430  * <p>
1431  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1432  * from which the running page was served.<br><br>
1433  * <p>
1434  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1435  * <p>
1436  * Be aware that to enable the browser to parse an XML document, the server must set
1437  * the Content-Type header in the HTTP response to "text/xml".
1438  * @constructor
1439  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1440  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
1441  * will be used to make the request.
1442  */
1443 Roo.data.HttpProxy = function(conn){
1444     Roo.data.HttpProxy.superclass.constructor.call(this);
1445     // is conn a conn config or a real conn?
1446     this.conn = conn;
1447     this.useAjax = !conn || !conn.events;
1448   
1449 };
1450
1451 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1452     // thse are take from connection...
1453     
1454     /**
1455      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1456      */
1457     /**
1458      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1459      * extra parameters to each request made by this object. (defaults to undefined)
1460      */
1461     /**
1462      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1463      *  to each request made by this object. (defaults to undefined)
1464      */
1465     /**
1466      * @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)
1467      */
1468     /**
1469      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1470      */
1471      /**
1472      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1473      * @type Boolean
1474      */
1475   
1476
1477     /**
1478      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1479      * @type Boolean
1480      */
1481     /**
1482      * Return the {@link Roo.data.Connection} object being used by this Proxy.
1483      * @return {Connection} The Connection object. This object may be used to subscribe to events on
1484      * a finer-grained basis than the DataProxy events.
1485      */
1486     getConnection : function(){
1487         return this.useAjax ? Roo.Ajax : this.conn;
1488     },
1489
1490     /**
1491      * Load data from the configured {@link Roo.data.Connection}, read the data object into
1492      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1493      * process that block using the passed callback.
1494      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1495      * for the request to the remote server.
1496      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1497      * object into a block of Roo.data.Records.
1498      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1499      * The function must be passed <ul>
1500      * <li>The Record block object</li>
1501      * <li>The "arg" argument from the load function</li>
1502      * <li>A boolean success indicator</li>
1503      * </ul>
1504      * @param {Object} scope The scope in which to call the callback
1505      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1506      */
1507     load : function(params, reader, callback, scope, arg){
1508         if(this.fireEvent("beforeload", this, params) !== false){
1509             var  o = {
1510                 params : params || {},
1511                 request: {
1512                     callback : callback,
1513                     scope : scope,
1514                     arg : arg
1515                 },
1516                 reader: reader,
1517                 callback : this.loadResponse,
1518                 scope: this
1519             };
1520             if(this.useAjax){
1521                 Roo.applyIf(o, this.conn);
1522                 if(this.activeRequest){
1523                     Roo.Ajax.abort(this.activeRequest);
1524                 }
1525                 this.activeRequest = Roo.Ajax.request(o);
1526             }else{
1527                 this.conn.request(o);
1528             }
1529         }else{
1530             callback.call(scope||this, null, arg, false);
1531         }
1532     },
1533
1534     // private
1535     loadResponse : function(o, success, response){
1536         delete this.activeRequest;
1537         if(!success){
1538             this.fireEvent("loadexception", this, o, response);
1539             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1540             return;
1541         }
1542         var result;
1543         try {
1544             result = o.reader.read(response);
1545         }catch(e){
1546             this.fireEvent("loadexception", this, o, response, e);
1547             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1548             return;
1549         }
1550         
1551         this.fireEvent("load", this, o, o.request.arg);
1552         o.request.callback.call(o.request.scope, result, o.request.arg, true);
1553     },
1554
1555     // private
1556     update : function(dataSet){
1557
1558     },
1559
1560     // private
1561     updateResponse : function(dataSet){
1562
1563     }
1564 });/*
1565  * Based on:
1566  * Ext JS Library 1.1.1
1567  * Copyright(c) 2006-2007, Ext JS, LLC.
1568  *
1569  * Originally Released Under LGPL - original licence link has changed is not relivant.
1570  *
1571  * Fork - LGPL
1572  * <script type="text/javascript">
1573  */
1574
1575 /**
1576  * @class Roo.data.ScriptTagProxy
1577  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1578  * other than the originating domain of the running page.<br><br>
1579  * <p>
1580  * <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
1581  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1582  * <p>
1583  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1584  * source code that is used as the source inside a &lt;script> tag.<br><br>
1585  * <p>
1586  * In order for the browser to process the returned data, the server must wrap the data object
1587  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1588  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1589  * depending on whether the callback name was passed:
1590  * <p>
1591  * <pre><code>
1592 boolean scriptTag = false;
1593 String cb = request.getParameter("callback");
1594 if (cb != null) {
1595     scriptTag = true;
1596     response.setContentType("text/javascript");
1597 } else {
1598     response.setContentType("application/x-json");
1599 }
1600 Writer out = response.getWriter();
1601 if (scriptTag) {
1602     out.write(cb + "(");
1603 }
1604 out.print(dataBlock.toJsonString());
1605 if (scriptTag) {
1606     out.write(");");
1607 }
1608 </pre></code>
1609  *
1610  * @constructor
1611  * @param {Object} config A configuration object.
1612  */
1613 Roo.data.ScriptTagProxy = function(config){
1614     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1615     Roo.apply(this, config);
1616     this.head = document.getElementsByTagName("head")[0];
1617 };
1618
1619 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1620
1621 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1622     /**
1623      * @cfg {String} url The URL from which to request the data object.
1624      */
1625     /**
1626      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1627      */
1628     timeout : 30000,
1629     /**
1630      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1631      * the server the name of the callback function set up by the load call to process the returned data object.
1632      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1633      * javascript output which calls this named function passing the data object as its only parameter.
1634      */
1635     callbackParam : "callback",
1636     /**
1637      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1638      * name to the request.
1639      */
1640     nocache : true,
1641
1642     /**
1643      * Load data from the configured URL, read the data object into
1644      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1645      * process that block using the passed callback.
1646      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1647      * for the request to the remote server.
1648      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1649      * object into a block of Roo.data.Records.
1650      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1651      * The function must be passed <ul>
1652      * <li>The Record block object</li>
1653      * <li>The "arg" argument from the load function</li>
1654      * <li>A boolean success indicator</li>
1655      * </ul>
1656      * @param {Object} scope The scope in which to call the callback
1657      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1658      */
1659     load : function(params, reader, callback, scope, arg){
1660         if(this.fireEvent("beforeload", this, params) !== false){
1661
1662             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1663
1664             var url = this.url;
1665             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1666             if(this.nocache){
1667                 url += "&_dc=" + (new Date().getTime());
1668             }
1669             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1670             var trans = {
1671                 id : transId,
1672                 cb : "stcCallback"+transId,
1673                 scriptId : "stcScript"+transId,
1674                 params : params,
1675                 arg : arg,
1676                 url : url,
1677                 callback : callback,
1678                 scope : scope,
1679                 reader : reader
1680             };
1681             var conn = this;
1682
1683             window[trans.cb] = function(o){
1684                 conn.handleResponse(o, trans);
1685             };
1686
1687             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1688
1689             if(this.autoAbort !== false){
1690                 this.abort();
1691             }
1692
1693             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1694
1695             var script = document.createElement("script");
1696             script.setAttribute("src", url);
1697             script.setAttribute("type", "text/javascript");
1698             script.setAttribute("id", trans.scriptId);
1699             this.head.appendChild(script);
1700
1701             this.trans = trans;
1702         }else{
1703             callback.call(scope||this, null, arg, false);
1704         }
1705     },
1706
1707     // private
1708     isLoading : function(){
1709         return this.trans ? true : false;
1710     },
1711
1712     /**
1713      * Abort the current server request.
1714      */
1715     abort : function(){
1716         if(this.isLoading()){
1717             this.destroyTrans(this.trans);
1718         }
1719     },
1720
1721     // private
1722     destroyTrans : function(trans, isLoaded){
1723         this.head.removeChild(document.getElementById(trans.scriptId));
1724         clearTimeout(trans.timeoutId);
1725         if(isLoaded){
1726             window[trans.cb] = undefined;
1727             try{
1728                 delete window[trans.cb];
1729             }catch(e){}
1730         }else{
1731             // if hasn't been loaded, wait for load to remove it to prevent script error
1732             window[trans.cb] = function(){
1733                 window[trans.cb] = undefined;
1734                 try{
1735                     delete window[trans.cb];
1736                 }catch(e){}
1737             };
1738         }
1739     },
1740
1741     // private
1742     handleResponse : function(o, trans){
1743         this.trans = false;
1744         this.destroyTrans(trans, true);
1745         var result;
1746         try {
1747             result = trans.reader.readRecords(o);
1748         }catch(e){
1749             this.fireEvent("loadexception", this, o, trans.arg, e);
1750             trans.callback.call(trans.scope||window, null, trans.arg, false);
1751             return;
1752         }
1753         this.fireEvent("load", this, o, trans.arg);
1754         trans.callback.call(trans.scope||window, result, trans.arg, true);
1755     },
1756
1757     // private
1758     handleFailure : function(trans){
1759         this.trans = false;
1760         this.destroyTrans(trans, false);
1761         this.fireEvent("loadexception", this, null, trans.arg);
1762         trans.callback.call(trans.scope||window, null, trans.arg, false);
1763     }
1764 });/*
1765  * Based on:
1766  * Ext JS Library 1.1.1
1767  * Copyright(c) 2006-2007, Ext JS, LLC.
1768  *
1769  * Originally Released Under LGPL - original licence link has changed is not relivant.
1770  *
1771  * Fork - LGPL
1772  * <script type="text/javascript">
1773  */
1774
1775 /**
1776  * @class Roo.data.JsonReader
1777  * @extends Roo.data.DataReader
1778  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1779  * based on mappings in a provided Roo.data.Record constructor.
1780  * 
1781  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1782  * in the reply previously. 
1783  * 
1784  * <p>
1785  * Example code:
1786  * <pre><code>
1787 var RecordDef = Roo.data.Record.create([
1788     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1789     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
1790 ]);
1791 var myReader = new Roo.data.JsonReader({
1792     totalProperty: "results",    // The property which contains the total dataset size (optional)
1793     root: "rows",                // The property which contains an Array of row objects
1794     id: "id"                     // The property within each row object that provides an ID for the record (optional)
1795 }, RecordDef);
1796 </code></pre>
1797  * <p>
1798  * This would consume a JSON file like this:
1799  * <pre><code>
1800 { 'results': 2, 'rows': [
1801     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1802     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1803 }
1804 </code></pre>
1805  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1806  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1807  * paged from the remote server.
1808  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1809  * @cfg {String} root name of the property which contains the Array of row objects.
1810  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1811  * @cfg {Array} fields Array of field definition objects
1812  * @constructor
1813  * Create a new JsonReader
1814  * @param {Object} meta Metadata configuration options
1815  * @param {Object} recordType Either an Array of field definition objects,
1816  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1817  */
1818 Roo.data.JsonReader = function(meta, recordType){
1819     
1820     meta = meta || {};
1821     // set some defaults:
1822     Roo.applyIf(meta, {
1823         totalProperty: 'total',
1824         successProperty : 'success',
1825         root : 'data',
1826         id : 'id'
1827     });
1828     
1829     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1830 };
1831 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1832     
1833     /**
1834      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
1835      * Used by Store query builder to append _requestMeta to params.
1836      * 
1837      */
1838     metaFromRemote : false,
1839     /**
1840      * This method is only used by a DataProxy which has retrieved data from a remote server.
1841      * @param {Object} response The XHR object which contains the JSON data in its responseText.
1842      * @return {Object} data A data block which is used by an Roo.data.Store object as
1843      * a cache of Roo.data.Records.
1844      */
1845     read : function(response){
1846         var json = response.responseText;
1847        
1848         var o = /* eval:var:o */ eval("("+json+")");
1849         if(!o) {
1850             throw {message: "JsonReader.read: Json object not found"};
1851         }
1852         
1853         if(o.metaData){
1854             
1855             delete this.ef;
1856             this.metaFromRemote = true;
1857             this.meta = o.metaData;
1858             this.recordType = Roo.data.Record.create(o.metaData.fields);
1859             this.onMetaChange(this.meta, this.recordType, o);
1860         }
1861         return this.readRecords(o);
1862     },
1863
1864     // private function a store will implement
1865     onMetaChange : function(meta, recordType, o){
1866
1867     },
1868
1869     /**
1870          * @ignore
1871          */
1872     simpleAccess: function(obj, subsc) {
1873         return obj[subsc];
1874     },
1875
1876         /**
1877          * @ignore
1878          */
1879     getJsonAccessor: function(){
1880         var re = /[\[\.]/;
1881         return function(expr) {
1882             try {
1883                 return(re.test(expr))
1884                     ? new Function("obj", "return obj." + expr)
1885                     : function(obj){
1886                         return obj[expr];
1887                     };
1888             } catch(e){}
1889             return Roo.emptyFn;
1890         };
1891     }(),
1892
1893     /**
1894      * Create a data block containing Roo.data.Records from an XML document.
1895      * @param {Object} o An object which contains an Array of row objects in the property specified
1896      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1897      * which contains the total size of the dataset.
1898      * @return {Object} data A data block which is used by an Roo.data.Store object as
1899      * a cache of Roo.data.Records.
1900      */
1901     readRecords : function(o){
1902         /**
1903          * After any data loads, the raw JSON data is available for further custom processing.
1904          * @type Object
1905          */
1906         this.o = o;
1907         var s = this.meta, Record = this.recordType,
1908             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1909
1910 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
1911         if (!this.ef) {
1912             if(s.totalProperty) {
1913                     this.getTotal = this.getJsonAccessor(s.totalProperty);
1914                 }
1915                 if(s.successProperty) {
1916                     this.getSuccess = this.getJsonAccessor(s.successProperty);
1917                 }
1918                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1919                 if (s.id) {
1920                         var g = this.getJsonAccessor(s.id);
1921                         this.getId = function(rec) {
1922                                 var r = g(rec);  
1923                                 return (r === undefined || r === "") ? null : r;
1924                         };
1925                 } else {
1926                         this.getId = function(){return null;};
1927                 }
1928             this.ef = [];
1929             for(var jj = 0; jj < fl; jj++){
1930                 f = fi[jj];
1931                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1932                 this.ef[jj] = this.getJsonAccessor(map);
1933             }
1934         }
1935
1936         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1937         if(s.totalProperty){
1938             var vt = parseInt(this.getTotal(o), 10);
1939             if(!isNaN(vt)){
1940                 totalRecords = vt;
1941             }
1942         }
1943         if(s.successProperty){
1944             var vs = this.getSuccess(o);
1945             if(vs === false || vs === 'false'){
1946                 success = false;
1947             }
1948         }
1949         var records = [];
1950         for(var i = 0; i < c; i++){
1951                 var n = root[i];
1952             var values = {};
1953             var id = this.getId(n);
1954             for(var j = 0; j < fl; j++){
1955                 f = fi[j];
1956             var v = this.ef[j](n);
1957             if (!f.convert) {
1958                 Roo.log('missing convert for ' + f.name);
1959                 Roo.log(f);
1960                 continue;
1961             }
1962             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1963             }
1964             var record = new Record(values, id);
1965             record.json = n;
1966             records[i] = record;
1967         }
1968         return {
1969             raw : o,
1970             success : success,
1971             records : records,
1972             totalRecords : totalRecords
1973         };
1974     }
1975 });/*
1976  * Based on:
1977  * Ext JS Library 1.1.1
1978  * Copyright(c) 2006-2007, Ext JS, LLC.
1979  *
1980  * Originally Released Under LGPL - original licence link has changed is not relivant.
1981  *
1982  * Fork - LGPL
1983  * <script type="text/javascript">
1984  */
1985
1986 /**
1987  * @class Roo.data.XmlReader
1988  * @extends Roo.data.DataReader
1989  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
1990  * based on mappings in a provided Roo.data.Record constructor.<br><br>
1991  * <p>
1992  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
1993  * header in the HTTP response must be set to "text/xml".</em>
1994  * <p>
1995  * Example code:
1996  * <pre><code>
1997 var RecordDef = Roo.data.Record.create([
1998    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1999    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
2000 ]);
2001 var myReader = new Roo.data.XmlReader({
2002    totalRecords: "results", // The element which contains the total dataset size (optional)
2003    record: "row",           // The repeated element which contains row information
2004    id: "id"                 // The element within the row that provides an ID for the record (optional)
2005 }, RecordDef);
2006 </code></pre>
2007  * <p>
2008  * This would consume an XML file like this:
2009  * <pre><code>
2010 &lt;?xml?>
2011 &lt;dataset>
2012  &lt;results>2&lt;/results>
2013  &lt;row>
2014    &lt;id>1&lt;/id>
2015    &lt;name>Bill&lt;/name>
2016    &lt;occupation>Gardener&lt;/occupation>
2017  &lt;/row>
2018  &lt;row>
2019    &lt;id>2&lt;/id>
2020    &lt;name>Ben&lt;/name>
2021    &lt;occupation>Horticulturalist&lt;/occupation>
2022  &lt;/row>
2023 &lt;/dataset>
2024 </code></pre>
2025  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2026  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2027  * paged from the remote server.
2028  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2029  * @cfg {String} success The DomQuery path to the success attribute used by forms.
2030  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2031  * a record identifier value.
2032  * @constructor
2033  * Create a new XmlReader
2034  * @param {Object} meta Metadata configuration options
2035  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
2036  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2037  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
2038  */
2039 Roo.data.XmlReader = function(meta, recordType){
2040     meta = meta || {};
2041     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2042 };
2043 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2044     /**
2045      * This method is only used by a DataProxy which has retrieved data from a remote server.
2046          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
2047          * to contain a method called 'responseXML' that returns an XML document object.
2048      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2049      * a cache of Roo.data.Records.
2050      */
2051     read : function(response){
2052         var doc = response.responseXML;
2053         if(!doc) {
2054             throw {message: "XmlReader.read: XML Document not available"};
2055         }
2056         return this.readRecords(doc);
2057     },
2058
2059     /**
2060      * Create a data block containing Roo.data.Records from an XML document.
2061          * @param {Object} doc A parsed XML document.
2062      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2063      * a cache of Roo.data.Records.
2064      */
2065     readRecords : function(doc){
2066         /**
2067          * After any data loads/reads, the raw XML Document is available for further custom processing.
2068          * @type XMLDocument
2069          */
2070         this.xmlData = doc;
2071         var root = doc.documentElement || doc;
2072         var q = Roo.DomQuery;
2073         var recordType = this.recordType, fields = recordType.prototype.fields;
2074         var sid = this.meta.id;
2075         var totalRecords = 0, success = true;
2076         if(this.meta.totalRecords){
2077             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2078         }
2079         
2080         if(this.meta.success){
2081             var sv = q.selectValue(this.meta.success, root, true);
2082             success = sv !== false && sv !== 'false';
2083         }
2084         var records = [];
2085         var ns = q.select(this.meta.record, root);
2086         for(var i = 0, len = ns.length; i < len; i++) {
2087                 var n = ns[i];
2088                 var values = {};
2089                 var id = sid ? q.selectValue(sid, n) : undefined;
2090                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2091                     var f = fields.items[j];
2092                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2093                     v = f.convert(v);
2094                     values[f.name] = v;
2095                 }
2096                 var record = new recordType(values, id);
2097                 record.node = n;
2098                 records[records.length] = record;
2099             }
2100
2101             return {
2102                 success : success,
2103                 records : records,
2104                 totalRecords : totalRecords || records.length
2105             };
2106     }
2107 });/*
2108  * Based on:
2109  * Ext JS Library 1.1.1
2110  * Copyright(c) 2006-2007, Ext JS, LLC.
2111  *
2112  * Originally Released Under LGPL - original licence link has changed is not relivant.
2113  *
2114  * Fork - LGPL
2115  * <script type="text/javascript">
2116  */
2117
2118 /**
2119  * @class Roo.data.ArrayReader
2120  * @extends Roo.data.DataReader
2121  * Data reader class to create an Array of Roo.data.Record objects from an Array.
2122  * Each element of that Array represents a row of data fields. The
2123  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2124  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2125  * <p>
2126  * Example code:.
2127  * <pre><code>
2128 var RecordDef = Roo.data.Record.create([
2129     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
2130     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
2131 ]);
2132 var myReader = new Roo.data.ArrayReader({
2133     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
2134 }, RecordDef);
2135 </code></pre>
2136  * <p>
2137  * This would consume an Array like this:
2138  * <pre><code>
2139 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2140   </code></pre>
2141  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
2142  * @constructor
2143  * Create a new JsonReader
2144  * @param {Object} meta Metadata configuration options.
2145  * @param {Object} recordType Either an Array of field definition objects
2146  * as specified to {@link Roo.data.Record#create},
2147  * or an {@link Roo.data.Record} object
2148  * created using {@link Roo.data.Record#create}.
2149  */
2150 Roo.data.ArrayReader = function(meta, recordType){
2151     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
2152 };
2153
2154 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2155     /**
2156      * Create a data block containing Roo.data.Records from an XML document.
2157      * @param {Object} o An Array of row objects which represents the dataset.
2158      * @return {Object} data A data block which is used by an Roo.data.Store object as
2159      * a cache of Roo.data.Records.
2160      */
2161     readRecords : function(o){
2162         var sid = this.meta ? this.meta.id : null;
2163         var recordType = this.recordType, fields = recordType.prototype.fields;
2164         var records = [];
2165         var root = o;
2166             for(var i = 0; i < root.length; i++){
2167                     var n = root[i];
2168                 var values = {};
2169                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2170                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2171                 var f = fields.items[j];
2172                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2173                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2174                 v = f.convert(v);
2175                 values[f.name] = v;
2176             }
2177                 var record = new recordType(values, id);
2178                 record.json = n;
2179                 records[records.length] = record;
2180             }
2181             return {
2182                 records : records,
2183                 totalRecords : records.length
2184             };
2185     }
2186 });/*
2187  * Based on:
2188  * Ext JS Library 1.1.1
2189  * Copyright(c) 2006-2007, Ext JS, LLC.
2190  *
2191  * Originally Released Under LGPL - original licence link has changed is not relivant.
2192  *
2193  * Fork - LGPL
2194  * <script type="text/javascript">
2195  */
2196
2197
2198 /**
2199  * @class Roo.data.Tree
2200  * @extends Roo.util.Observable
2201  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2202  * in the tree have most standard DOM functionality.
2203  * @constructor
2204  * @param {Node} root (optional) The root node
2205  */
2206 Roo.data.Tree = function(root){
2207    this.nodeHash = {};
2208    /**
2209     * The root node for this tree
2210     * @type Node
2211     */
2212    this.root = null;
2213    if(root){
2214        this.setRootNode(root);
2215    }
2216    this.addEvents({
2217        /**
2218         * @event append
2219         * Fires when a new child node is appended to a node in this tree.
2220         * @param {Tree} tree The owner tree
2221         * @param {Node} parent The parent node
2222         * @param {Node} node The newly appended node
2223         * @param {Number} index The index of the newly appended node
2224         */
2225        "append" : true,
2226        /**
2227         * @event remove
2228         * Fires when a child node is removed from a node in this tree.
2229         * @param {Tree} tree The owner tree
2230         * @param {Node} parent The parent node
2231         * @param {Node} node The child node removed
2232         */
2233        "remove" : true,
2234        /**
2235         * @event move
2236         * Fires when a node is moved to a new location in the tree
2237         * @param {Tree} tree The owner tree
2238         * @param {Node} node The node moved
2239         * @param {Node} oldParent The old parent of this node
2240         * @param {Node} newParent The new parent of this node
2241         * @param {Number} index The index it was moved to
2242         */
2243        "move" : true,
2244        /**
2245         * @event insert
2246         * Fires when a new child node is inserted in a node in this tree.
2247         * @param {Tree} tree The owner tree
2248         * @param {Node} parent The parent node
2249         * @param {Node} node The child node inserted
2250         * @param {Node} refNode The child node the node was inserted before
2251         */
2252        "insert" : true,
2253        /**
2254         * @event beforeappend
2255         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2256         * @param {Tree} tree The owner tree
2257         * @param {Node} parent The parent node
2258         * @param {Node} node The child node to be appended
2259         */
2260        "beforeappend" : true,
2261        /**
2262         * @event beforeremove
2263         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2264         * @param {Tree} tree The owner tree
2265         * @param {Node} parent The parent node
2266         * @param {Node} node The child node to be removed
2267         */
2268        "beforeremove" : true,
2269        /**
2270         * @event beforemove
2271         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2272         * @param {Tree} tree The owner tree
2273         * @param {Node} node The node being moved
2274         * @param {Node} oldParent The parent of the node
2275         * @param {Node} newParent The new parent the node is moving to
2276         * @param {Number} index The index it is being moved to
2277         */
2278        "beforemove" : true,
2279        /**
2280         * @event beforeinsert
2281         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2282         * @param {Tree} tree The owner tree
2283         * @param {Node} parent The parent node
2284         * @param {Node} node The child node to be inserted
2285         * @param {Node} refNode The child node the node is being inserted before
2286         */
2287        "beforeinsert" : true
2288    });
2289
2290     Roo.data.Tree.superclass.constructor.call(this);
2291 };
2292
2293 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2294     pathSeparator: "/",
2295
2296     proxyNodeEvent : function(){
2297         return this.fireEvent.apply(this, arguments);
2298     },
2299
2300     /**
2301      * Returns the root node for this tree.
2302      * @return {Node}
2303      */
2304     getRootNode : function(){
2305         return this.root;
2306     },
2307
2308     /**
2309      * Sets the root node for this tree.
2310      * @param {Node} node
2311      * @return {Node}
2312      */
2313     setRootNode : function(node){
2314         this.root = node;
2315         node.ownerTree = this;
2316         node.isRoot = true;
2317         this.registerNode(node);
2318         return node;
2319     },
2320
2321     /**
2322      * Gets a node in this tree by its id.
2323      * @param {String} id
2324      * @return {Node}
2325      */
2326     getNodeById : function(id){
2327         return this.nodeHash[id];
2328     },
2329
2330     registerNode : function(node){
2331         this.nodeHash[node.id] = node;
2332     },
2333
2334     unregisterNode : function(node){
2335         delete this.nodeHash[node.id];
2336     },
2337
2338     toString : function(){
2339         return "[Tree"+(this.id?" "+this.id:"")+"]";
2340     }
2341 });
2342
2343 /**
2344  * @class Roo.data.Node
2345  * @extends Roo.util.Observable
2346  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2347  * @cfg {String} id The id for this node. If one is not specified, one is generated.
2348  * @constructor
2349  * @param {Object} attributes The attributes/config for the node
2350  */
2351 Roo.data.Node = function(attributes){
2352     /**
2353      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2354      * @type {Object}
2355      */
2356     this.attributes = attributes || {};
2357     this.leaf = this.attributes.leaf;
2358     /**
2359      * The node id. @type String
2360      */
2361     this.id = this.attributes.id;
2362     if(!this.id){
2363         this.id = Roo.id(null, "ynode-");
2364         this.attributes.id = this.id;
2365     }
2366      
2367     
2368     /**
2369      * All child nodes of this node. @type Array
2370      */
2371     this.childNodes = [];
2372     if(!this.childNodes.indexOf){ // indexOf is a must
2373         this.childNodes.indexOf = function(o){
2374             for(var i = 0, len = this.length; i < len; i++){
2375                 if(this[i] == o) {
2376                     return i;
2377                 }
2378             }
2379             return -1;
2380         };
2381     }
2382     /**
2383      * The parent node for this node. @type Node
2384      */
2385     this.parentNode = null;
2386     /**
2387      * The first direct child node of this node, or null if this node has no child nodes. @type Node
2388      */
2389     this.firstChild = null;
2390     /**
2391      * The last direct child node of this node, or null if this node has no child nodes. @type Node
2392      */
2393     this.lastChild = null;
2394     /**
2395      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2396      */
2397     this.previousSibling = null;
2398     /**
2399      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2400      */
2401     this.nextSibling = null;
2402
2403     this.addEvents({
2404        /**
2405         * @event append
2406         * Fires when a new child node is appended
2407         * @param {Tree} tree The owner tree
2408         * @param {Node} this This node
2409         * @param {Node} node The newly appended node
2410         * @param {Number} index The index of the newly appended node
2411         */
2412        "append" : true,
2413        /**
2414         * @event remove
2415         * Fires when a child node is removed
2416         * @param {Tree} tree The owner tree
2417         * @param {Node} this This node
2418         * @param {Node} node The removed node
2419         */
2420        "remove" : true,
2421        /**
2422         * @event move
2423         * Fires when this node is moved to a new location in the tree
2424         * @param {Tree} tree The owner tree
2425         * @param {Node} this This node
2426         * @param {Node} oldParent The old parent of this node
2427         * @param {Node} newParent The new parent of this node
2428         * @param {Number} index The index it was moved to
2429         */
2430        "move" : true,
2431        /**
2432         * @event insert
2433         * Fires when a new child node is inserted.
2434         * @param {Tree} tree The owner tree
2435         * @param {Node} this This node
2436         * @param {Node} node The child node inserted
2437         * @param {Node} refNode The child node the node was inserted before
2438         */
2439        "insert" : true,
2440        /**
2441         * @event beforeappend
2442         * Fires before a new child is appended, return false to cancel the append.
2443         * @param {Tree} tree The owner tree
2444         * @param {Node} this This node
2445         * @param {Node} node The child node to be appended
2446         */
2447        "beforeappend" : true,
2448        /**
2449         * @event beforeremove
2450         * Fires before a child is removed, return false to cancel the remove.
2451         * @param {Tree} tree The owner tree
2452         * @param {Node} this This node
2453         * @param {Node} node The child node to be removed
2454         */
2455        "beforeremove" : true,
2456        /**
2457         * @event beforemove
2458         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2459         * @param {Tree} tree The owner tree
2460         * @param {Node} this This node
2461         * @param {Node} oldParent The parent of this node
2462         * @param {Node} newParent The new parent this node is moving to
2463         * @param {Number} index The index it is being moved to
2464         */
2465        "beforemove" : true,
2466        /**
2467         * @event beforeinsert
2468         * Fires before a new child is inserted, return false to cancel the insert.
2469         * @param {Tree} tree The owner tree
2470         * @param {Node} this This node
2471         * @param {Node} node The child node to be inserted
2472         * @param {Node} refNode The child node the node is being inserted before
2473         */
2474        "beforeinsert" : true
2475    });
2476     this.listeners = this.attributes.listeners;
2477     Roo.data.Node.superclass.constructor.call(this);
2478 };
2479
2480 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2481     fireEvent : function(evtName){
2482         // first do standard event for this node
2483         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2484             return false;
2485         }
2486         // then bubble it up to the tree if the event wasn't cancelled
2487         var ot = this.getOwnerTree();
2488         if(ot){
2489             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2490                 return false;
2491             }
2492         }
2493         return true;
2494     },
2495
2496     /**
2497      * Returns true if this node is a leaf
2498      * @return {Boolean}
2499      */
2500     isLeaf : function(){
2501         return this.leaf === true;
2502     },
2503
2504     // private
2505     setFirstChild : function(node){
2506         this.firstChild = node;
2507     },
2508
2509     //private
2510     setLastChild : function(node){
2511         this.lastChild = node;
2512     },
2513
2514
2515     /**
2516      * Returns true if this node is the last child of its parent
2517      * @return {Boolean}
2518      */
2519     isLast : function(){
2520        return (!this.parentNode ? true : this.parentNode.lastChild == this);
2521     },
2522
2523     /**
2524      * Returns true if this node is the first child of its parent
2525      * @return {Boolean}
2526      */
2527     isFirst : function(){
2528        return (!this.parentNode ? true : this.parentNode.firstChild == this);
2529     },
2530
2531     hasChildNodes : function(){
2532         return !this.isLeaf() && this.childNodes.length > 0;
2533     },
2534
2535     /**
2536      * Insert node(s) as the last child node of this node.
2537      * @param {Node/Array} node The node or Array of nodes to append
2538      * @return {Node} The appended node if single append, or null if an array was passed
2539      */
2540     appendChild : function(node){
2541         var multi = false;
2542         if(node instanceof Array){
2543             multi = node;
2544         }else if(arguments.length > 1){
2545             multi = arguments;
2546         }
2547         // if passed an array or multiple args do them one by one
2548         if(multi){
2549             for(var i = 0, len = multi.length; i < len; i++) {
2550                 this.appendChild(multi[i]);
2551             }
2552         }else{
2553             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2554                 return false;
2555             }
2556             var index = this.childNodes.length;
2557             var oldParent = node.parentNode;
2558             // it's a move, make sure we move it cleanly
2559             if(oldParent){
2560                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2561                     return false;
2562                 }
2563                 oldParent.removeChild(node);
2564             }
2565             index = this.childNodes.length;
2566             if(index == 0){
2567                 this.setFirstChild(node);
2568             }
2569             this.childNodes.push(node);
2570             node.parentNode = this;
2571             var ps = this.childNodes[index-1];
2572             if(ps){
2573                 node.previousSibling = ps;
2574                 ps.nextSibling = node;
2575             }else{
2576                 node.previousSibling = null;
2577             }
2578             node.nextSibling = null;
2579             this.setLastChild(node);
2580             node.setOwnerTree(this.getOwnerTree());
2581             this.fireEvent("append", this.ownerTree, this, node, index);
2582             if(oldParent){
2583                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2584             }
2585             return node;
2586         }
2587     },
2588
2589     /**
2590      * Removes a child node from this node.
2591      * @param {Node} node The node to remove
2592      * @return {Node} The removed node
2593      */
2594     removeChild : function(node){
2595         var index = this.childNodes.indexOf(node);
2596         if(index == -1){
2597             return false;
2598         }
2599         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2600             return false;
2601         }
2602
2603         // remove it from childNodes collection
2604         this.childNodes.splice(index, 1);
2605
2606         // update siblings
2607         if(node.previousSibling){
2608             node.previousSibling.nextSibling = node.nextSibling;
2609         }
2610         if(node.nextSibling){
2611             node.nextSibling.previousSibling = node.previousSibling;
2612         }
2613
2614         // update child refs
2615         if(this.firstChild == node){
2616             this.setFirstChild(node.nextSibling);
2617         }
2618         if(this.lastChild == node){
2619             this.setLastChild(node.previousSibling);
2620         }
2621
2622         node.setOwnerTree(null);
2623         // clear any references from the node
2624         node.parentNode = null;
2625         node.previousSibling = null;
2626         node.nextSibling = null;
2627         this.fireEvent("remove", this.ownerTree, this, node);
2628         return node;
2629     },
2630
2631     /**
2632      * Inserts the first node before the second node in this nodes childNodes collection.
2633      * @param {Node} node The node to insert
2634      * @param {Node} refNode The node to insert before (if null the node is appended)
2635      * @return {Node} The inserted node
2636      */
2637     insertBefore : function(node, refNode){
2638         if(!refNode){ // like standard Dom, refNode can be null for append
2639             return this.appendChild(node);
2640         }
2641         // nothing to do
2642         if(node == refNode){
2643             return false;
2644         }
2645
2646         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2647             return false;
2648         }
2649         var index = this.childNodes.indexOf(refNode);
2650         var oldParent = node.parentNode;
2651         var refIndex = index;
2652
2653         // when moving internally, indexes will change after remove
2654         if(oldParent == this && this.childNodes.indexOf(node) < index){
2655             refIndex--;
2656         }
2657
2658         // it's a move, make sure we move it cleanly
2659         if(oldParent){
2660             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2661                 return false;
2662             }
2663             oldParent.removeChild(node);
2664         }
2665         if(refIndex == 0){
2666             this.setFirstChild(node);
2667         }
2668         this.childNodes.splice(refIndex, 0, node);
2669         node.parentNode = this;
2670         var ps = this.childNodes[refIndex-1];
2671         if(ps){
2672             node.previousSibling = ps;
2673             ps.nextSibling = node;
2674         }else{
2675             node.previousSibling = null;
2676         }
2677         node.nextSibling = refNode;
2678         refNode.previousSibling = node;
2679         node.setOwnerTree(this.getOwnerTree());
2680         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2681         if(oldParent){
2682             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2683         }
2684         return node;
2685     },
2686
2687     /**
2688      * Returns the child node at the specified index.
2689      * @param {Number} index
2690      * @return {Node}
2691      */
2692     item : function(index){
2693         return this.childNodes[index];
2694     },
2695
2696     /**
2697      * Replaces one child node in this node with another.
2698      * @param {Node} newChild The replacement node
2699      * @param {Node} oldChild The node to replace
2700      * @return {Node} The replaced node
2701      */
2702     replaceChild : function(newChild, oldChild){
2703         this.insertBefore(newChild, oldChild);
2704         this.removeChild(oldChild);
2705         return oldChild;
2706     },
2707
2708     /**
2709      * Returns the index of a child node
2710      * @param {Node} node
2711      * @return {Number} The index of the node or -1 if it was not found
2712      */
2713     indexOf : function(child){
2714         return this.childNodes.indexOf(child);
2715     },
2716
2717     /**
2718      * Returns the tree this node is in.
2719      * @return {Tree}
2720      */
2721     getOwnerTree : function(){
2722         // if it doesn't have one, look for one
2723         if(!this.ownerTree){
2724             var p = this;
2725             while(p){
2726                 if(p.ownerTree){
2727                     this.ownerTree = p.ownerTree;
2728                     break;
2729                 }
2730                 p = p.parentNode;
2731             }
2732         }
2733         return this.ownerTree;
2734     },
2735
2736     /**
2737      * Returns depth of this node (the root node has a depth of 0)
2738      * @return {Number}
2739      */
2740     getDepth : function(){
2741         var depth = 0;
2742         var p = this;
2743         while(p.parentNode){
2744             ++depth;
2745             p = p.parentNode;
2746         }
2747         return depth;
2748     },
2749
2750     // private
2751     setOwnerTree : function(tree){
2752         // if it's move, we need to update everyone
2753         if(tree != this.ownerTree){
2754             if(this.ownerTree){
2755                 this.ownerTree.unregisterNode(this);
2756             }
2757             this.ownerTree = tree;
2758             var cs = this.childNodes;
2759             for(var i = 0, len = cs.length; i < len; i++) {
2760                 cs[i].setOwnerTree(tree);
2761             }
2762             if(tree){
2763                 tree.registerNode(this);
2764             }
2765         }
2766     },
2767
2768     /**
2769      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2770      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2771      * @return {String} The path
2772      */
2773     getPath : function(attr){
2774         attr = attr || "id";
2775         var p = this.parentNode;
2776         var b = [this.attributes[attr]];
2777         while(p){
2778             b.unshift(p.attributes[attr]);
2779             p = p.parentNode;
2780         }
2781         var sep = this.getOwnerTree().pathSeparator;
2782         return sep + b.join(sep);
2783     },
2784
2785     /**
2786      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2787      * function call will be the scope provided or the current node. The arguments to the function
2788      * will be the args provided or the current node. If the function returns false at any point,
2789      * the bubble is stopped.
2790      * @param {Function} fn The function to call
2791      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2792      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2793      */
2794     bubble : function(fn, scope, args){
2795         var p = this;
2796         while(p){
2797             if(fn.call(scope || p, args || p) === false){
2798                 break;
2799             }
2800             p = p.parentNode;
2801         }
2802     },
2803
2804     /**
2805      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2806      * function call will be the scope provided or the current node. The arguments to the function
2807      * will be the args provided or the current node. If the function returns false at any point,
2808      * the cascade is stopped on that branch.
2809      * @param {Function} fn The function to call
2810      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2811      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2812      */
2813     cascade : function(fn, scope, args){
2814         if(fn.call(scope || this, args || this) !== false){
2815             var cs = this.childNodes;
2816             for(var i = 0, len = cs.length; i < len; i++) {
2817                 cs[i].cascade(fn, scope, args);
2818             }
2819         }
2820     },
2821
2822     /**
2823      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2824      * function call will be the scope provided or the current node. The arguments to the function
2825      * will be the args provided or the current node. If the function returns false at any point,
2826      * the iteration stops.
2827      * @param {Function} fn The function to call
2828      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2829      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2830      */
2831     eachChild : function(fn, scope, args){
2832         var cs = this.childNodes;
2833         for(var i = 0, len = cs.length; i < len; i++) {
2834                 if(fn.call(scope || this, args || cs[i]) === false){
2835                     break;
2836                 }
2837         }
2838     },
2839
2840     /**
2841      * Finds the first child that has the attribute with the specified value.
2842      * @param {String} attribute The attribute name
2843      * @param {Mixed} value The value to search for
2844      * @return {Node} The found child or null if none was found
2845      */
2846     findChild : function(attribute, value){
2847         var cs = this.childNodes;
2848         for(var i = 0, len = cs.length; i < len; i++) {
2849                 if(cs[i].attributes[attribute] == value){
2850                     return cs[i];
2851                 }
2852         }
2853         return null;
2854     },
2855
2856     /**
2857      * Finds the first child by a custom function. The child matches if the function passed
2858      * returns true.
2859      * @param {Function} fn
2860      * @param {Object} scope (optional)
2861      * @return {Node} The found child or null if none was found
2862      */
2863     findChildBy : function(fn, scope){
2864         var cs = this.childNodes;
2865         for(var i = 0, len = cs.length; i < len; i++) {
2866                 if(fn.call(scope||cs[i], cs[i]) === true){
2867                     return cs[i];
2868                 }
2869         }
2870         return null;
2871     },
2872
2873     /**
2874      * Sorts this nodes children using the supplied sort function
2875      * @param {Function} fn
2876      * @param {Object} scope (optional)
2877      */
2878     sort : function(fn, scope){
2879         var cs = this.childNodes;
2880         var len = cs.length;
2881         if(len > 0){
2882             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2883             cs.sort(sortFn);
2884             for(var i = 0; i < len; i++){
2885                 var n = cs[i];
2886                 n.previousSibling = cs[i-1];
2887                 n.nextSibling = cs[i+1];
2888                 if(i == 0){
2889                     this.setFirstChild(n);
2890                 }
2891                 if(i == len-1){
2892                     this.setLastChild(n);
2893                 }
2894             }
2895         }
2896     },
2897
2898     /**
2899      * Returns true if this node is an ancestor (at any point) of the passed node.
2900      * @param {Node} node
2901      * @return {Boolean}
2902      */
2903     contains : function(node){
2904         return node.isAncestor(this);
2905     },
2906
2907     /**
2908      * Returns true if the passed node is an ancestor (at any point) of this node.
2909      * @param {Node} node
2910      * @return {Boolean}
2911      */
2912     isAncestor : function(node){
2913         var p = this.parentNode;
2914         while(p){
2915             if(p == node){
2916                 return true;
2917             }
2918             p = p.parentNode;
2919         }
2920         return false;
2921     },
2922
2923     toString : function(){
2924         return "[Node"+(this.id?" "+this.id:"")+"]";
2925     }
2926 });/*
2927  * Based on:
2928  * Ext JS Library 1.1.1
2929  * Copyright(c) 2006-2007, Ext JS, LLC.
2930  *
2931  * Originally Released Under LGPL - original licence link has changed is not relivant.
2932  *
2933  * Fork - LGPL
2934  * <script type="text/javascript">
2935  */
2936  (function(){ 
2937 /**
2938  * @class Roo.Layer
2939  * @extends Roo.Element
2940  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
2941  * automatic maintaining of shadow/shim positions.
2942  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
2943  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
2944  * you can pass a string with a CSS class name. False turns off the shadow.
2945  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
2946  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
2947  * @cfg {String} cls CSS class to add to the element
2948  * @cfg {Number} zindex Starting z-index (defaults to 11000)
2949  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
2950  * @constructor
2951  * @param {Object} config An object with config options.
2952  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
2953  */
2954
2955 Roo.Layer = function(config, existingEl){
2956     config = config || {};
2957     var dh = Roo.DomHelper;
2958     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
2959     if(existingEl){
2960         this.dom = Roo.getDom(existingEl);
2961     }
2962     if(!this.dom){
2963         var o = config.dh || {tag: "div", cls: "x-layer"};
2964         this.dom = dh.append(pel, o);
2965     }
2966     if(config.cls){
2967         this.addClass(config.cls);
2968     }
2969     this.constrain = config.constrain !== false;
2970     this.visibilityMode = Roo.Element.VISIBILITY;
2971     if(config.id){
2972         this.id = this.dom.id = config.id;
2973     }else{
2974         this.id = Roo.id(this.dom);
2975     }
2976     this.zindex = config.zindex || this.getZIndex();
2977     this.position("absolute", this.zindex);
2978     if(config.shadow){
2979         this.shadowOffset = config.shadowOffset || 4;
2980         this.shadow = new Roo.Shadow({
2981             offset : this.shadowOffset,
2982             mode : config.shadow
2983         });
2984     }else{
2985         this.shadowOffset = 0;
2986     }
2987     this.useShim = config.shim !== false && Roo.useShims;
2988     this.useDisplay = config.useDisplay;
2989     this.hide();
2990 };
2991
2992 var supr = Roo.Element.prototype;
2993
2994 // shims are shared among layer to keep from having 100 iframes
2995 var shims = [];
2996
2997 Roo.extend(Roo.Layer, Roo.Element, {
2998
2999     getZIndex : function(){
3000         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
3001     },
3002
3003     getShim : function(){
3004         if(!this.useShim){
3005             return null;
3006         }
3007         if(this.shim){
3008             return this.shim;
3009         }
3010         var shim = shims.shift();
3011         if(!shim){
3012             shim = this.createShim();
3013             shim.enableDisplayMode('block');
3014             shim.dom.style.display = 'none';
3015             shim.dom.style.visibility = 'visible';
3016         }
3017         var pn = this.dom.parentNode;
3018         if(shim.dom.parentNode != pn){
3019             pn.insertBefore(shim.dom, this.dom);
3020         }
3021         shim.setStyle('z-index', this.getZIndex()-2);
3022         this.shim = shim;
3023         return shim;
3024     },
3025
3026     hideShim : function(){
3027         if(this.shim){
3028             this.shim.setDisplayed(false);
3029             shims.push(this.shim);
3030             delete this.shim;
3031         }
3032     },
3033
3034     disableShadow : function(){
3035         if(this.shadow){
3036             this.shadowDisabled = true;
3037             this.shadow.hide();
3038             this.lastShadowOffset = this.shadowOffset;
3039             this.shadowOffset = 0;
3040         }
3041     },
3042
3043     enableShadow : function(show){
3044         if(this.shadow){
3045             this.shadowDisabled = false;
3046             this.shadowOffset = this.lastShadowOffset;
3047             delete this.lastShadowOffset;
3048             if(show){
3049                 this.sync(true);
3050             }
3051         }
3052     },
3053
3054     // private
3055     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
3056     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
3057     sync : function(doShow){
3058         var sw = this.shadow;
3059         if(!this.updating && this.isVisible() && (sw || this.useShim)){
3060             var sh = this.getShim();
3061
3062             var w = this.getWidth(),
3063                 h = this.getHeight();
3064
3065             var l = this.getLeft(true),
3066                 t = this.getTop(true);
3067
3068             if(sw && !this.shadowDisabled){
3069                 if(doShow && !sw.isVisible()){
3070                     sw.show(this);
3071                 }else{
3072                     sw.realign(l, t, w, h);
3073                 }
3074                 if(sh){
3075                     if(doShow){
3076                        sh.show();
3077                     }
3078                     // fit the shim behind the shadow, so it is shimmed too
3079                     var a = sw.adjusts, s = sh.dom.style;
3080                     s.left = (Math.min(l, l+a.l))+"px";
3081                     s.top = (Math.min(t, t+a.t))+"px";
3082                     s.width = (w+a.w)+"px";
3083                     s.height = (h+a.h)+"px";
3084                 }
3085             }else if(sh){
3086                 if(doShow){
3087                    sh.show();
3088                 }
3089                 sh.setSize(w, h);
3090                 sh.setLeftTop(l, t);
3091             }
3092             
3093         }
3094     },
3095
3096     // private
3097     destroy : function(){
3098         this.hideShim();
3099         if(this.shadow){
3100             this.shadow.hide();
3101         }
3102         this.removeAllListeners();
3103         var pn = this.dom.parentNode;
3104         if(pn){
3105             pn.removeChild(this.dom);
3106         }
3107         Roo.Element.uncache(this.id);
3108     },
3109
3110     remove : function(){
3111         this.destroy();
3112     },
3113
3114     // private
3115     beginUpdate : function(){
3116         this.updating = true;
3117     },
3118
3119     // private
3120     endUpdate : function(){
3121         this.updating = false;
3122         this.sync(true);
3123     },
3124
3125     // private
3126     hideUnders : function(negOffset){
3127         if(this.shadow){
3128             this.shadow.hide();
3129         }
3130         this.hideShim();
3131     },
3132
3133     // private
3134     constrainXY : function(){
3135         if(this.constrain){
3136             var vw = Roo.lib.Dom.getViewWidth(),
3137                 vh = Roo.lib.Dom.getViewHeight();
3138             var s = Roo.get(document).getScroll();
3139
3140             var xy = this.getXY();
3141             var x = xy[0], y = xy[1];   
3142             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
3143             // only move it if it needs it
3144             var moved = false;
3145             // first validate right/bottom
3146             if((x + w) > vw+s.left){
3147                 x = vw - w - this.shadowOffset;
3148                 moved = true;
3149             }
3150             if((y + h) > vh+s.top){
3151                 y = vh - h - this.shadowOffset;
3152                 moved = true;
3153             }
3154             // then make sure top/left isn't negative
3155             if(x < s.left){
3156                 x = s.left;
3157                 moved = true;
3158             }
3159             if(y < s.top){
3160                 y = s.top;
3161                 moved = true;
3162             }
3163             if(moved){
3164                 if(this.avoidY){
3165                     var ay = this.avoidY;
3166                     if(y <= ay && (y+h) >= ay){
3167                         y = ay-h-5;   
3168                     }
3169                 }
3170                 xy = [x, y];
3171                 this.storeXY(xy);
3172                 supr.setXY.call(this, xy);
3173                 this.sync();
3174             }
3175         }
3176     },
3177
3178     isVisible : function(){
3179         return this.visible;    
3180     },
3181
3182     // private
3183     showAction : function(){
3184         this.visible = true; // track visibility to prevent getStyle calls
3185         if(this.useDisplay === true){
3186             this.setDisplayed("");
3187         }else if(this.lastXY){
3188             supr.setXY.call(this, this.lastXY);
3189         }else if(this.lastLT){
3190             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
3191         }
3192     },
3193
3194     // private
3195     hideAction : function(){
3196         this.visible = false;
3197         if(this.useDisplay === true){
3198             this.setDisplayed(false);
3199         }else{
3200             this.setLeftTop(-10000,-10000);
3201         }
3202     },
3203
3204     // overridden Element method
3205     setVisible : function(v, a, d, c, e){
3206         if(v){
3207             this.showAction();
3208         }
3209         if(a && v){
3210             var cb = function(){
3211                 this.sync(true);
3212                 if(c){
3213                     c();
3214                 }
3215             }.createDelegate(this);
3216             supr.setVisible.call(this, true, true, d, cb, e);
3217         }else{
3218             if(!v){
3219                 this.hideUnders(true);
3220             }
3221             var cb = c;
3222             if(a){
3223                 cb = function(){
3224                     this.hideAction();
3225                     if(c){
3226                         c();
3227                     }
3228                 }.createDelegate(this);
3229             }
3230             supr.setVisible.call(this, v, a, d, cb, e);
3231             if(v){
3232                 this.sync(true);
3233             }else if(!a){
3234                 this.hideAction();
3235             }
3236         }
3237     },
3238
3239     storeXY : function(xy){
3240         delete this.lastLT;
3241         this.lastXY = xy;
3242     },
3243
3244     storeLeftTop : function(left, top){
3245         delete this.lastXY;
3246         this.lastLT = [left, top];
3247     },
3248
3249     // private
3250     beforeFx : function(){
3251         this.beforeAction();
3252         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
3253     },
3254
3255     // private
3256     afterFx : function(){
3257         Roo.Layer.superclass.afterFx.apply(this, arguments);
3258         this.sync(this.isVisible());
3259     },
3260
3261     // private
3262     beforeAction : function(){
3263         if(!this.updating && this.shadow){
3264             this.shadow.hide();
3265         }
3266     },
3267
3268     // overridden Element method
3269     setLeft : function(left){
3270         this.storeLeftTop(left, this.getTop(true));
3271         supr.setLeft.apply(this, arguments);
3272         this.sync();
3273     },
3274
3275     setTop : function(top){
3276         this.storeLeftTop(this.getLeft(true), top);
3277         supr.setTop.apply(this, arguments);
3278         this.sync();
3279     },
3280
3281     setLeftTop : function(left, top){
3282         this.storeLeftTop(left, top);
3283         supr.setLeftTop.apply(this, arguments);
3284         this.sync();
3285     },
3286
3287     setXY : function(xy, a, d, c, e){
3288         this.fixDisplay();
3289         this.beforeAction();
3290         this.storeXY(xy);
3291         var cb = this.createCB(c);
3292         supr.setXY.call(this, xy, a, d, cb, e);
3293         if(!a){
3294             cb();
3295         }
3296     },
3297
3298     // private
3299     createCB : function(c){
3300         var el = this;
3301         return function(){
3302             el.constrainXY();
3303             el.sync(true);
3304             if(c){
3305                 c();
3306             }
3307         };
3308     },
3309
3310     // overridden Element method
3311     setX : function(x, a, d, c, e){
3312         this.setXY([x, this.getY()], a, d, c, e);
3313     },
3314
3315     // overridden Element method
3316     setY : function(y, a, d, c, e){
3317         this.setXY([this.getX(), y], a, d, c, e);
3318     },
3319
3320     // overridden Element method
3321     setSize : function(w, h, a, d, c, e){
3322         this.beforeAction();
3323         var cb = this.createCB(c);
3324         supr.setSize.call(this, w, h, a, d, cb, e);
3325         if(!a){
3326             cb();
3327         }
3328     },
3329
3330     // overridden Element method
3331     setWidth : function(w, a, d, c, e){
3332         this.beforeAction();
3333         var cb = this.createCB(c);
3334         supr.setWidth.call(this, w, a, d, cb, e);
3335         if(!a){
3336             cb();
3337         }
3338     },
3339
3340     // overridden Element method
3341     setHeight : function(h, a, d, c, e){
3342         this.beforeAction();
3343         var cb = this.createCB(c);
3344         supr.setHeight.call(this, h, a, d, cb, e);
3345         if(!a){
3346             cb();
3347         }
3348     },
3349
3350     // overridden Element method
3351     setBounds : function(x, y, w, h, a, d, c, e){
3352         this.beforeAction();
3353         var cb = this.createCB(c);
3354         if(!a){
3355             this.storeXY([x, y]);
3356             supr.setXY.call(this, [x, y]);
3357             supr.setSize.call(this, w, h, a, d, cb, e);
3358             cb();
3359         }else{
3360             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
3361         }
3362         return this;
3363     },
3364     
3365     /**
3366      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
3367      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
3368      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
3369      * @param {Number} zindex The new z-index to set
3370      * @return {this} The Layer
3371      */
3372     setZIndex : function(zindex){
3373         this.zindex = zindex;
3374         this.setStyle("z-index", zindex + 2);
3375         if(this.shadow){
3376             this.shadow.setZIndex(zindex + 1);
3377         }
3378         if(this.shim){
3379             this.shim.setStyle("z-index", zindex);
3380         }
3381     }
3382 });
3383 })();/*
3384  * Based on:
3385  * Ext JS Library 1.1.1
3386  * Copyright(c) 2006-2007, Ext JS, LLC.
3387  *
3388  * Originally Released Under LGPL - original licence link has changed is not relivant.
3389  *
3390  * Fork - LGPL
3391  * <script type="text/javascript">
3392  */
3393
3394
3395 /**
3396  * @class Roo.Shadow
3397  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
3398  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
3399  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3400  * @constructor
3401  * Create a new Shadow
3402  * @param {Object} config The config object
3403  */
3404 Roo.Shadow = function(config){
3405     Roo.apply(this, config);
3406     if(typeof this.mode != "string"){
3407         this.mode = this.defaultMode;
3408     }
3409     var o = this.offset, a = {h: 0};
3410     var rad = Math.floor(this.offset/2);
3411     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3412         case "drop":
3413             a.w = 0;
3414             a.l = a.t = o;
3415             a.t -= 1;
3416             if(Roo.isIE){
3417                 a.l -= this.offset + rad;
3418                 a.t -= this.offset + rad;
3419                 a.w -= rad;
3420                 a.h -= rad;
3421                 a.t += 1;
3422             }
3423         break;
3424         case "sides":
3425             a.w = (o*2);
3426             a.l = -o;
3427             a.t = o-1;
3428             if(Roo.isIE){
3429                 a.l -= (this.offset - rad);
3430                 a.t -= this.offset + rad;
3431                 a.l += 1;
3432                 a.w -= (this.offset - rad)*2;
3433                 a.w -= rad + 1;
3434                 a.h -= 1;
3435             }
3436         break;
3437         case "frame":
3438             a.w = a.h = (o*2);
3439             a.l = a.t = -o;
3440             a.t += 1;
3441             a.h -= 2;
3442             if(Roo.isIE){
3443                 a.l -= (this.offset - rad);
3444                 a.t -= (this.offset - rad);
3445                 a.l += 1;
3446                 a.w -= (this.offset + rad + 1);
3447                 a.h -= (this.offset + rad);
3448                 a.h += 1;
3449             }
3450         break;
3451     };
3452
3453     this.adjusts = a;
3454 };
3455
3456 Roo.Shadow.prototype = {
3457     /**
3458      * @cfg {String} mode
3459      * The shadow display mode.  Supports the following options:<br />
3460      * sides: Shadow displays on both sides and bottom only<br />
3461      * frame: Shadow displays equally on all four sides<br />
3462      * drop: Traditional bottom-right drop shadow (default)
3463      */
3464     /**
3465      * @cfg {String} offset
3466      * The number of pixels to offset the shadow from the element (defaults to 4)
3467      */
3468     offset: 4,
3469
3470     // private
3471     defaultMode: "drop",
3472
3473     /**
3474      * Displays the shadow under the target element
3475      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3476      */
3477     show : function(target){
3478         target = Roo.get(target);
3479         if(!this.el){
3480             this.el = Roo.Shadow.Pool.pull();
3481             if(this.el.dom.nextSibling != target.dom){
3482                 this.el.insertBefore(target);
3483             }
3484         }
3485         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3486         if(Roo.isIE){
3487             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3488         }
3489         this.realign(
3490             target.getLeft(true),
3491             target.getTop(true),
3492             target.getWidth(),
3493             target.getHeight()
3494         );
3495         this.el.dom.style.display = "block";
3496     },
3497
3498     /**
3499      * Returns true if the shadow is visible, else false
3500      */
3501     isVisible : function(){
3502         return this.el ? true : false;  
3503     },
3504
3505     /**
3506      * Direct alignment when values are already available. Show must be called at least once before
3507      * calling this method to ensure it is initialized.
3508      * @param {Number} left The target element left position
3509      * @param {Number} top The target element top position
3510      * @param {Number} width The target element width
3511      * @param {Number} height The target element height
3512      */
3513     realign : function(l, t, w, h){
3514         if(!this.el){
3515             return;
3516         }
3517         var a = this.adjusts, d = this.el.dom, s = d.style;
3518         var iea = 0;
3519         s.left = (l+a.l)+"px";
3520         s.top = (t+a.t)+"px";
3521         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3522  
3523         if(s.width != sws || s.height != shs){
3524             s.width = sws;
3525             s.height = shs;
3526             if(!Roo.isIE){
3527                 var cn = d.childNodes;
3528                 var sww = Math.max(0, (sw-12))+"px";
3529                 cn[0].childNodes[1].style.width = sww;
3530                 cn[1].childNodes[1].style.width = sww;
3531                 cn[2].childNodes[1].style.width = sww;
3532                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3533             }
3534         }
3535     },
3536
3537     /**
3538      * Hides this shadow
3539      */
3540     hide : function(){
3541         if(this.el){
3542             this.el.dom.style.display = "none";
3543             Roo.Shadow.Pool.push(this.el);
3544             delete this.el;
3545         }
3546     },
3547
3548     /**
3549      * Adjust the z-index of this shadow
3550      * @param {Number} zindex The new z-index
3551      */
3552     setZIndex : function(z){
3553         this.zIndex = z;
3554         if(this.el){
3555             this.el.setStyle("z-index", z);
3556         }
3557     }
3558 };
3559
3560 // Private utility class that manages the internal Shadow cache
3561 Roo.Shadow.Pool = function(){
3562     var p = [];
3563     var markup = Roo.isIE ?
3564                  '<div class="x-ie-shadow"></div>' :
3565                  '<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>';
3566     return {
3567         pull : function(){
3568             var sh = p.shift();
3569             if(!sh){
3570                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3571                 sh.autoBoxAdjust = false;
3572             }
3573             return sh;
3574         },
3575
3576         push : function(sh){
3577             p.push(sh);
3578         }
3579     };
3580 }();/*
3581  * Based on:
3582  * Ext JS Library 1.1.1
3583  * Copyright(c) 2006-2007, Ext JS, LLC.
3584  *
3585  * Originally Released Under LGPL - original licence link has changed is not relivant.
3586  *
3587  * Fork - LGPL
3588  * <script type="text/javascript">
3589  */
3590
3591
3592 /**
3593  * @class Roo.SplitBar
3594  * @extends Roo.util.Observable
3595  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3596  * <br><br>
3597  * Usage:
3598  * <pre><code>
3599 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3600                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3601 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3602 split.minSize = 100;
3603 split.maxSize = 600;
3604 split.animate = true;
3605 split.on('moved', splitterMoved);
3606 </code></pre>
3607  * @constructor
3608  * Create a new SplitBar
3609  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3610  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3611  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3612  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3613                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3614                         position of the SplitBar).
3615  */
3616 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3617     
3618     /** @private */
3619     this.el = Roo.get(dragElement, true);
3620     this.el.dom.unselectable = "on";
3621     /** @private */
3622     this.resizingEl = Roo.get(resizingElement, true);
3623
3624     /**
3625      * @private
3626      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3627      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3628      * @type Number
3629      */
3630     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3631     
3632     /**
3633      * The minimum size of the resizing element. (Defaults to 0)
3634      * @type Number
3635      */
3636     this.minSize = 0;
3637     
3638     /**
3639      * The maximum size of the resizing element. (Defaults to 2000)
3640      * @type Number
3641      */
3642     this.maxSize = 2000;
3643     
3644     /**
3645      * Whether to animate the transition to the new size
3646      * @type Boolean
3647      */
3648     this.animate = false;
3649     
3650     /**
3651      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3652      * @type Boolean
3653      */
3654     this.useShim = false;
3655     
3656     /** @private */
3657     this.shim = null;
3658     
3659     if(!existingProxy){
3660         /** @private */
3661         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3662     }else{
3663         this.proxy = Roo.get(existingProxy).dom;
3664     }
3665     /** @private */
3666     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3667     
3668     /** @private */
3669     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3670     
3671     /** @private */
3672     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3673     
3674     /** @private */
3675     this.dragSpecs = {};
3676     
3677     /**
3678      * @private The adapter to use to positon and resize elements
3679      */
3680     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3681     this.adapter.init(this);
3682     
3683     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3684         /** @private */
3685         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3686         this.el.addClass("x-splitbar-h");
3687     }else{
3688         /** @private */
3689         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3690         this.el.addClass("x-splitbar-v");
3691     }
3692     
3693     this.addEvents({
3694         /**
3695          * @event resize
3696          * Fires when the splitter is moved (alias for {@link #event-moved})
3697          * @param {Roo.SplitBar} this
3698          * @param {Number} newSize the new width or height
3699          */
3700         "resize" : true,
3701         /**
3702          * @event moved
3703          * Fires when the splitter is moved
3704          * @param {Roo.SplitBar} this
3705          * @param {Number} newSize the new width or height
3706          */
3707         "moved" : true,
3708         /**
3709          * @event beforeresize
3710          * Fires before the splitter is dragged
3711          * @param {Roo.SplitBar} this
3712          */
3713         "beforeresize" : true,
3714
3715         "beforeapply" : true
3716     });
3717
3718     Roo.util.Observable.call(this);
3719 };
3720
3721 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3722     onStartProxyDrag : function(x, y){
3723         this.fireEvent("beforeresize", this);
3724         if(!this.overlay){
3725             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3726             o.unselectable();
3727             o.enableDisplayMode("block");
3728             // all splitbars share the same overlay
3729             Roo.SplitBar.prototype.overlay = o;
3730         }
3731         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3732         this.overlay.show();
3733         Roo.get(this.proxy).setDisplayed("block");
3734         var size = this.adapter.getElementSize(this);
3735         this.activeMinSize = this.getMinimumSize();;
3736         this.activeMaxSize = this.getMaximumSize();;
3737         var c1 = size - this.activeMinSize;
3738         var c2 = Math.max(this.activeMaxSize - size, 0);
3739         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3740             this.dd.resetConstraints();
3741             this.dd.setXConstraint(
3742                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3743                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3744             );
3745             this.dd.setYConstraint(0, 0);
3746         }else{
3747             this.dd.resetConstraints();
3748             this.dd.setXConstraint(0, 0);
3749             this.dd.setYConstraint(
3750                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3751                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3752             );
3753          }
3754         this.dragSpecs.startSize = size;
3755         this.dragSpecs.startPoint = [x, y];
3756         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3757     },
3758     
3759     /** 
3760      * @private Called after the drag operation by the DDProxy
3761      */
3762     onEndProxyDrag : function(e){
3763         Roo.get(this.proxy).setDisplayed(false);
3764         var endPoint = Roo.lib.Event.getXY(e);
3765         if(this.overlay){
3766             this.overlay.hide();
3767         }
3768         var newSize;
3769         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3770             newSize = this.dragSpecs.startSize + 
3771                 (this.placement == Roo.SplitBar.LEFT ?
3772                     endPoint[0] - this.dragSpecs.startPoint[0] :
3773                     this.dragSpecs.startPoint[0] - endPoint[0]
3774                 );
3775         }else{
3776             newSize = this.dragSpecs.startSize + 
3777                 (this.placement == Roo.SplitBar.TOP ?
3778                     endPoint[1] - this.dragSpecs.startPoint[1] :
3779                     this.dragSpecs.startPoint[1] - endPoint[1]
3780                 );
3781         }
3782         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3783         if(newSize != this.dragSpecs.startSize){
3784             if(this.fireEvent('beforeapply', this, newSize) !== false){
3785                 this.adapter.setElementSize(this, newSize);
3786                 this.fireEvent("moved", this, newSize);
3787                 this.fireEvent("resize", this, newSize);
3788             }
3789         }
3790     },
3791     
3792     /**
3793      * Get the adapter this SplitBar uses
3794      * @return The adapter object
3795      */
3796     getAdapter : function(){
3797         return this.adapter;
3798     },
3799     
3800     /**
3801      * Set the adapter this SplitBar uses
3802      * @param {Object} adapter A SplitBar adapter object
3803      */
3804     setAdapter : function(adapter){
3805         this.adapter = adapter;
3806         this.adapter.init(this);
3807     },
3808     
3809     /**
3810      * Gets the minimum size for the resizing element
3811      * @return {Number} The minimum size
3812      */
3813     getMinimumSize : function(){
3814         return this.minSize;
3815     },
3816     
3817     /**
3818      * Sets the minimum size for the resizing element
3819      * @param {Number} minSize The minimum size
3820      */
3821     setMinimumSize : function(minSize){
3822         this.minSize = minSize;
3823     },
3824     
3825     /**
3826      * Gets the maximum size for the resizing element
3827      * @return {Number} The maximum size
3828      */
3829     getMaximumSize : function(){
3830         return this.maxSize;
3831     },
3832     
3833     /**
3834      * Sets the maximum size for the resizing element
3835      * @param {Number} maxSize The maximum size
3836      */
3837     setMaximumSize : function(maxSize){
3838         this.maxSize = maxSize;
3839     },
3840     
3841     /**
3842      * Sets the initialize size for the resizing element
3843      * @param {Number} size The initial size
3844      */
3845     setCurrentSize : function(size){
3846         var oldAnimate = this.animate;
3847         this.animate = false;
3848         this.adapter.setElementSize(this, size);
3849         this.animate = oldAnimate;
3850     },
3851     
3852     /**
3853      * Destroy this splitbar. 
3854      * @param {Boolean} removeEl True to remove the element
3855      */
3856     destroy : function(removeEl){
3857         if(this.shim){
3858             this.shim.remove();
3859         }
3860         this.dd.unreg();
3861         this.proxy.parentNode.removeChild(this.proxy);
3862         if(removeEl){
3863             this.el.remove();
3864         }
3865     }
3866 });
3867
3868 /**
3869  * @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.
3870  */
3871 Roo.SplitBar.createProxy = function(dir){
3872     var proxy = new Roo.Element(document.createElement("div"));
3873     proxy.unselectable();
3874     var cls = 'x-splitbar-proxy';
3875     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3876     document.body.appendChild(proxy.dom);
3877     return proxy.dom;
3878 };
3879
3880 /** 
3881  * @class Roo.SplitBar.BasicLayoutAdapter
3882  * Default Adapter. It assumes the splitter and resizing element are not positioned
3883  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3884  */
3885 Roo.SplitBar.BasicLayoutAdapter = function(){
3886 };
3887
3888 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3889     // do nothing for now
3890     init : function(s){
3891     
3892     },
3893     /**
3894      * Called before drag operations to get the current size of the resizing element. 
3895      * @param {Roo.SplitBar} s The SplitBar using this adapter
3896      */
3897      getElementSize : function(s){
3898         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3899             return s.resizingEl.getWidth();
3900         }else{
3901             return s.resizingEl.getHeight();
3902         }
3903     },
3904     
3905     /**
3906      * Called after drag operations to set the size of the resizing element.
3907      * @param {Roo.SplitBar} s The SplitBar using this adapter
3908      * @param {Number} newSize The new size to set
3909      * @param {Function} onComplete A function to be invoked when resizing is complete
3910      */
3911     setElementSize : function(s, newSize, onComplete){
3912         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3913             if(!s.animate){
3914                 s.resizingEl.setWidth(newSize);
3915                 if(onComplete){
3916                     onComplete(s, newSize);
3917                 }
3918             }else{
3919                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3920             }
3921         }else{
3922             
3923             if(!s.animate){
3924                 s.resizingEl.setHeight(newSize);
3925                 if(onComplete){
3926                     onComplete(s, newSize);
3927                 }
3928             }else{
3929                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3930             }
3931         }
3932     }
3933 };
3934
3935 /** 
3936  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3937  * @extends Roo.SplitBar.BasicLayoutAdapter
3938  * Adapter that  moves the splitter element to align with the resized sizing element. 
3939  * Used with an absolute positioned SplitBar.
3940  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3941  * document.body, make sure you assign an id to the body element.
3942  */
3943 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3944     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3945     this.container = Roo.get(container);
3946 };
3947
3948 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3949     init : function(s){
3950         this.basic.init(s);
3951     },
3952     
3953     getElementSize : function(s){
3954         return this.basic.getElementSize(s);
3955     },
3956     
3957     setElementSize : function(s, newSize, onComplete){
3958         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3959     },
3960     
3961     moveSplitter : function(s){
3962         var yes = Roo.SplitBar;
3963         switch(s.placement){
3964             case yes.LEFT:
3965                 s.el.setX(s.resizingEl.getRight());
3966                 break;
3967             case yes.RIGHT:
3968                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3969                 break;
3970             case yes.TOP:
3971                 s.el.setY(s.resizingEl.getBottom());
3972                 break;
3973             case yes.BOTTOM:
3974                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3975                 break;
3976         }
3977     }
3978 };
3979
3980 /**
3981  * Orientation constant - Create a vertical SplitBar
3982  * @static
3983  * @type Number
3984  */
3985 Roo.SplitBar.VERTICAL = 1;
3986
3987 /**
3988  * Orientation constant - Create a horizontal SplitBar
3989  * @static
3990  * @type Number
3991  */
3992 Roo.SplitBar.HORIZONTAL = 2;
3993
3994 /**
3995  * Placement constant - The resizing element is to the left of the splitter element
3996  * @static
3997  * @type Number
3998  */
3999 Roo.SplitBar.LEFT = 1;
4000
4001 /**
4002  * Placement constant - The resizing element is to the right of the splitter element
4003  * @static
4004  * @type Number
4005  */
4006 Roo.SplitBar.RIGHT = 2;
4007
4008 /**
4009  * Placement constant - The resizing element is positioned above the splitter element
4010  * @static
4011  * @type Number
4012  */
4013 Roo.SplitBar.TOP = 3;
4014
4015 /**
4016  * Placement constant - The resizing element is positioned under splitter element
4017  * @static
4018  * @type Number
4019  */
4020 Roo.SplitBar.BOTTOM = 4;
4021 /*
4022  * Based on:
4023  * Ext JS Library 1.1.1
4024  * Copyright(c) 2006-2007, Ext JS, LLC.
4025  *
4026  * Originally Released Under LGPL - original licence link has changed is not relivant.
4027  *
4028  * Fork - LGPL
4029  * <script type="text/javascript">
4030  */
4031
4032 /**
4033  * @class Roo.View
4034  * @extends Roo.util.Observable
4035  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
4036  * This class also supports single and multi selection modes. <br>
4037  * Create a data model bound view:
4038  <pre><code>
4039  var store = new Roo.data.Store(...);
4040
4041  var view = new Roo.View({
4042     el : "my-element",
4043     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
4044  
4045     singleSelect: true,
4046     selectedClass: "ydataview-selected",
4047     store: store
4048  });
4049
4050  // listen for node click?
4051  view.on("click", function(vw, index, node, e){
4052  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4053  });
4054
4055  // load XML data
4056  dataModel.load("foobar.xml");
4057  </code></pre>
4058  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
4059  * <br><br>
4060  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
4061  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
4062  * 
4063  * Note: old style constructor is still suported (container, template, config)
4064  * 
4065  * @constructor
4066  * Create a new View
4067  * @param {Object} config The config object
4068  * 
4069  */
4070 Roo.View = function(config, depreciated_tpl, depreciated_config){
4071     
4072     this.parent = false;
4073     
4074     if (typeof(depreciated_tpl) == 'undefined') {
4075         // new way.. - universal constructor.
4076         Roo.apply(this, config);
4077         this.el  = Roo.get(this.el);
4078     } else {
4079         // old format..
4080         this.el  = Roo.get(config);
4081         this.tpl = depreciated_tpl;
4082         Roo.apply(this, depreciated_config);
4083     }
4084     this.wrapEl  = this.el.wrap().wrap();
4085     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
4086     
4087     
4088     if(typeof(this.tpl) == "string"){
4089         this.tpl = new Roo.Template(this.tpl);
4090     } else {
4091         // support xtype ctors..
4092         this.tpl = new Roo.factory(this.tpl, Roo);
4093     }
4094     
4095     
4096     this.tpl.compile();
4097     
4098     /** @private */
4099     this.addEvents({
4100         /**
4101          * @event beforeclick
4102          * Fires before a click is processed. Returns false to cancel the default action.
4103          * @param {Roo.View} this
4104          * @param {Number} index The index of the target node
4105          * @param {HTMLElement} node The target node
4106          * @param {Roo.EventObject} e The raw event object
4107          */
4108             "beforeclick" : true,
4109         /**
4110          * @event click
4111          * Fires when a template node is clicked.
4112          * @param {Roo.View} this
4113          * @param {Number} index The index of the target node
4114          * @param {HTMLElement} node The target node
4115          * @param {Roo.EventObject} e The raw event object
4116          */
4117             "click" : true,
4118         /**
4119          * @event dblclick
4120          * Fires when a template node is double clicked.
4121          * @param {Roo.View} this
4122          * @param {Number} index The index of the target node
4123          * @param {HTMLElement} node The target node
4124          * @param {Roo.EventObject} e The raw event object
4125          */
4126             "dblclick" : true,
4127         /**
4128          * @event contextmenu
4129          * Fires when a template node is right clicked.
4130          * @param {Roo.View} this
4131          * @param {Number} index The index of the target node
4132          * @param {HTMLElement} node The target node
4133          * @param {Roo.EventObject} e The raw event object
4134          */
4135             "contextmenu" : true,
4136         /**
4137          * @event selectionchange
4138          * Fires when the selected nodes change.
4139          * @param {Roo.View} this
4140          * @param {Array} selections Array of the selected nodes
4141          */
4142             "selectionchange" : true,
4143     
4144         /**
4145          * @event beforeselect
4146          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
4147          * @param {Roo.View} this
4148          * @param {HTMLElement} node The node to be selected
4149          * @param {Array} selections Array of currently selected nodes
4150          */
4151             "beforeselect" : true,
4152         /**
4153          * @event preparedata
4154          * Fires on every row to render, to allow you to change the data.
4155          * @param {Roo.View} this
4156          * @param {Object} data to be rendered (change this)
4157          */
4158           "preparedata" : true
4159           
4160           
4161         });
4162
4163
4164
4165     this.el.on({
4166         "click": this.onClick,
4167         "dblclick": this.onDblClick,
4168         "contextmenu": this.onContextMenu,
4169         scope:this
4170     });
4171
4172     this.selections = [];
4173     this.nodes = [];
4174     this.cmp = new Roo.CompositeElementLite([]);
4175     if(this.store){
4176         this.store = Roo.factory(this.store, Roo.data);
4177         this.setStore(this.store, true);
4178     }
4179     
4180     if ( this.footer && this.footer.xtype) {
4181            
4182          var fctr = this.wrapEl.appendChild(document.createElement("div"));
4183         
4184         this.footer.dataSource = this.store;
4185         this.footer.container = fctr;
4186         this.footer = Roo.factory(this.footer, Roo);
4187         fctr.insertFirst(this.el);
4188         
4189         // this is a bit insane - as the paging toolbar seems to detach the el..
4190 //        dom.parentNode.parentNode.parentNode
4191          // they get detached?
4192     }
4193     
4194     
4195     Roo.View.superclass.constructor.call(this);
4196     
4197     
4198 };
4199
4200 Roo.extend(Roo.View, Roo.util.Observable, {
4201     
4202      /**
4203      * @cfg {Roo.data.Store} store Data store to load data from.
4204      */
4205     store : false,
4206     
4207     /**
4208      * @cfg {String|Roo.Element} el The container element.
4209      */
4210     el : '',
4211     
4212     /**
4213      * @cfg {String|Roo.Template} tpl The template used by this View 
4214      */
4215     tpl : false,
4216     /**
4217      * @cfg {String} dataName the named area of the template to use as the data area
4218      *                          Works with domtemplates roo-name="name"
4219      */
4220     dataName: false,
4221     /**
4222      * @cfg {String} selectedClass The css class to add to selected nodes
4223      */
4224     selectedClass : "x-view-selected",
4225      /**
4226      * @cfg {String} emptyText The empty text to show when nothing is loaded.
4227      */
4228     emptyText : "",
4229     
4230     /**
4231      * @cfg {String} text to display on mask (default Loading)
4232      */
4233     mask : false,
4234     /**
4235      * @cfg {Boolean} multiSelect Allow multiple selection
4236      */
4237     multiSelect : false,
4238     /**
4239      * @cfg {Boolean} singleSelect Allow single selection
4240      */
4241     singleSelect:  false,
4242     
4243     /**
4244      * @cfg {Boolean} toggleSelect - selecting 
4245      */
4246     toggleSelect : false,
4247     
4248     /**
4249      * @cfg {Boolean} tickable - selecting 
4250      */
4251     tickable : false,
4252     
4253     /**
4254      * Returns the element this view is bound to.
4255      * @return {Roo.Element}
4256      */
4257     getEl : function(){
4258         return this.wrapEl;
4259     },
4260     
4261     
4262
4263     /**
4264      * Refreshes the view. - called by datachanged on the store. - do not call directly.
4265      */
4266     refresh : function(){
4267         //Roo.log('refresh');
4268         var t = this.tpl;
4269         
4270         // if we are using something like 'domtemplate', then
4271         // the what gets used is:
4272         // t.applySubtemplate(NAME, data, wrapping data..)
4273         // the outer template then get' applied with
4274         //     the store 'extra data'
4275         // and the body get's added to the
4276         //      roo-name="data" node?
4277         //      <span class='roo-tpl-{name}'></span> ?????
4278         
4279         
4280         
4281         this.clearSelections();
4282         this.el.update("");
4283         var html = [];
4284         var records = this.store.getRange();
4285         if(records.length < 1) {
4286             
4287             // is this valid??  = should it render a template??
4288             
4289             this.el.update(this.emptyText);
4290             return;
4291         }
4292         var el = this.el;
4293         if (this.dataName) {
4294             this.el.update(t.apply(this.store.meta)); //????
4295             el = this.el.child('.roo-tpl-' + this.dataName);
4296         }
4297         
4298         for(var i = 0, len = records.length; i < len; i++){
4299             var data = this.prepareData(records[i].data, i, records[i]);
4300             this.fireEvent("preparedata", this, data, i, records[i]);
4301             
4302             var d = Roo.apply({}, data);
4303             
4304             if(this.tickable){
4305                 Roo.apply(d, {'roo-id' : Roo.id()});
4306                 
4307                 var _this = this;
4308             
4309                 Roo.each(this.parent.item, function(item){
4310                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
4311                         return;
4312                     }
4313                     Roo.apply(d, {'roo-data-checked' : 'checked'});
4314                 });
4315             }
4316             
4317             html[html.length] = Roo.util.Format.trim(
4318                 this.dataName ?
4319                     t.applySubtemplate(this.dataName, d, this.store.meta) :
4320                     t.apply(d)
4321             );
4322         }
4323         
4324         
4325         
4326         el.update(html.join(""));
4327         this.nodes = el.dom.childNodes;
4328         this.updateIndexes(0);
4329     },
4330     
4331
4332     /**
4333      * Function to override to reformat the data that is sent to
4334      * the template for each node.
4335      * DEPRICATED - use the preparedata event handler.
4336      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
4337      * a JSON object for an UpdateManager bound view).
4338      */
4339     prepareData : function(data, index, record)
4340     {
4341         this.fireEvent("preparedata", this, data, index, record);
4342         return data;
4343     },
4344
4345     onUpdate : function(ds, record){
4346         // Roo.log('on update');   
4347         this.clearSelections();
4348         var index = this.store.indexOf(record);
4349         var n = this.nodes[index];
4350         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
4351         n.parentNode.removeChild(n);
4352         this.updateIndexes(index, index);
4353     },
4354
4355     
4356     
4357 // --------- FIXME     
4358     onAdd : function(ds, records, index)
4359     {
4360         //Roo.log(['on Add', ds, records, index] );        
4361         this.clearSelections();
4362         if(this.nodes.length == 0){
4363             this.refresh();
4364             return;
4365         }
4366         var n = this.nodes[index];
4367         for(var i = 0, len = records.length; i < len; i++){
4368             var d = this.prepareData(records[i].data, i, records[i]);
4369             if(n){
4370                 this.tpl.insertBefore(n, d);
4371             }else{
4372                 
4373                 this.tpl.append(this.el, d);
4374             }
4375         }
4376         this.updateIndexes(index);
4377     },
4378
4379     onRemove : function(ds, record, index){
4380        // Roo.log('onRemove');
4381         this.clearSelections();
4382         var el = this.dataName  ?
4383             this.el.child('.roo-tpl-' + this.dataName) :
4384             this.el; 
4385         
4386         el.dom.removeChild(this.nodes[index]);
4387         this.updateIndexes(index);
4388     },
4389
4390     /**
4391      * Refresh an individual node.
4392      * @param {Number} index
4393      */
4394     refreshNode : function(index){
4395         this.onUpdate(this.store, this.store.getAt(index));
4396     },
4397
4398     updateIndexes : function(startIndex, endIndex){
4399         var ns = this.nodes;
4400         startIndex = startIndex || 0;
4401         endIndex = endIndex || ns.length - 1;
4402         for(var i = startIndex; i <= endIndex; i++){
4403             ns[i].nodeIndex = i;
4404         }
4405     },
4406
4407     /**
4408      * Changes the data store this view uses and refresh the view.
4409      * @param {Store} store
4410      */
4411     setStore : function(store, initial){
4412         if(!initial && this.store){
4413             this.store.un("datachanged", this.refresh);
4414             this.store.un("add", this.onAdd);
4415             this.store.un("remove", this.onRemove);
4416             this.store.un("update", this.onUpdate);
4417             this.store.un("clear", this.refresh);
4418             this.store.un("beforeload", this.onBeforeLoad);
4419             this.store.un("load", this.onLoad);
4420             this.store.un("loadexception", this.onLoad);
4421         }
4422         if(store){
4423           
4424             store.on("datachanged", this.refresh, this);
4425             store.on("add", this.onAdd, this);
4426             store.on("remove", this.onRemove, this);
4427             store.on("update", this.onUpdate, this);
4428             store.on("clear", this.refresh, this);
4429             store.on("beforeload", this.onBeforeLoad, this);
4430             store.on("load", this.onLoad, this);
4431             store.on("loadexception", this.onLoad, this);
4432         }
4433         
4434         if(store){
4435             this.refresh();
4436         }
4437     },
4438     /**
4439      * onbeforeLoad - masks the loading area.
4440      *
4441      */
4442     onBeforeLoad : function(store,opts)
4443     {
4444          //Roo.log('onBeforeLoad');   
4445         if (!opts.add) {
4446             this.el.update("");
4447         }
4448         this.el.mask(this.mask ? this.mask : "Loading" ); 
4449     },
4450     onLoad : function ()
4451     {
4452         this.el.unmask();
4453     },
4454     
4455
4456     /**
4457      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4458      * @param {HTMLElement} node
4459      * @return {HTMLElement} The template node
4460      */
4461     findItemFromChild : function(node){
4462         var el = this.dataName  ?
4463             this.el.child('.roo-tpl-' + this.dataName,true) :
4464             this.el.dom; 
4465         
4466         if(!node || node.parentNode == el){
4467                     return node;
4468             }
4469             var p = node.parentNode;
4470             while(p && p != el){
4471             if(p.parentNode == el){
4472                 return p;
4473             }
4474             p = p.parentNode;
4475         }
4476             return null;
4477     },
4478
4479     /** @ignore */
4480     onClick : function(e){
4481         var item = this.findItemFromChild(e.getTarget());
4482         if(item){
4483             var index = this.indexOf(item);
4484             if(this.onItemClick(item, index, e) !== false){
4485                 this.fireEvent("click", this, index, item, e);
4486             }
4487         }else{
4488             this.clearSelections();
4489         }
4490     },
4491
4492     /** @ignore */
4493     onContextMenu : function(e){
4494         var item = this.findItemFromChild(e.getTarget());
4495         if(item){
4496             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4497         }
4498     },
4499
4500     /** @ignore */
4501     onDblClick : function(e){
4502         var item = this.findItemFromChild(e.getTarget());
4503         if(item){
4504             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4505         }
4506     },
4507
4508     onItemClick : function(item, index, e)
4509     {
4510         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4511             return false;
4512         }
4513         if (this.toggleSelect) {
4514             var m = this.isSelected(item) ? 'unselect' : 'select';
4515             //Roo.log(m);
4516             var _t = this;
4517             _t[m](item, true, false);
4518             return true;
4519         }
4520         if(this.multiSelect || this.singleSelect){
4521             if(this.multiSelect && e.shiftKey && this.lastSelection){
4522                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4523             }else{
4524                 this.select(item, this.multiSelect && e.ctrlKey);
4525                 this.lastSelection = item;
4526             }
4527             
4528             if(!this.tickable){
4529                 e.preventDefault();
4530             }
4531             
4532         }
4533         return true;
4534     },
4535
4536     /**
4537      * Get the number of selected nodes.
4538      * @return {Number}
4539      */
4540     getSelectionCount : function(){
4541         return this.selections.length;
4542     },
4543
4544     /**
4545      * Get the currently selected nodes.
4546      * @return {Array} An array of HTMLElements
4547      */
4548     getSelectedNodes : function(){
4549         return this.selections;
4550     },
4551
4552     /**
4553      * Get the indexes of the selected nodes.
4554      * @return {Array}
4555      */
4556     getSelectedIndexes : function(){
4557         var indexes = [], s = this.selections;
4558         for(var i = 0, len = s.length; i < len; i++){
4559             indexes.push(s[i].nodeIndex);
4560         }
4561         return indexes;
4562     },
4563
4564     /**
4565      * Clear all selections
4566      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4567      */
4568     clearSelections : function(suppressEvent){
4569         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4570             this.cmp.elements = this.selections;
4571             this.cmp.removeClass(this.selectedClass);
4572             this.selections = [];
4573             if(!suppressEvent){
4574                 this.fireEvent("selectionchange", this, this.selections);
4575             }
4576         }
4577     },
4578
4579     /**
4580      * Returns true if the passed node is selected
4581      * @param {HTMLElement/Number} node The node or node index
4582      * @return {Boolean}
4583      */
4584     isSelected : function(node){
4585         var s = this.selections;
4586         if(s.length < 1){
4587             return false;
4588         }
4589         node = this.getNode(node);
4590         return s.indexOf(node) !== -1;
4591     },
4592
4593     /**
4594      * Selects nodes.
4595      * @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
4596      * @param {Boolean} keepExisting (optional) true to keep existing selections
4597      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4598      */
4599     select : function(nodeInfo, keepExisting, suppressEvent){
4600         if(nodeInfo instanceof Array){
4601             if(!keepExisting){
4602                 this.clearSelections(true);
4603             }
4604             for(var i = 0, len = nodeInfo.length; i < len; i++){
4605                 this.select(nodeInfo[i], true, true);
4606             }
4607             return;
4608         } 
4609         var node = this.getNode(nodeInfo);
4610         if(!node || this.isSelected(node)){
4611             return; // already selected.
4612         }
4613         if(!keepExisting){
4614             this.clearSelections(true);
4615         }
4616         
4617         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4618             Roo.fly(node).addClass(this.selectedClass);
4619             this.selections.push(node);
4620             if(!suppressEvent){
4621                 this.fireEvent("selectionchange", this, this.selections);
4622             }
4623         }
4624         
4625         
4626     },
4627       /**
4628      * Unselects nodes.
4629      * @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
4630      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4631      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4632      */
4633     unselect : function(nodeInfo, keepExisting, suppressEvent)
4634     {
4635         if(nodeInfo instanceof Array){
4636             Roo.each(this.selections, function(s) {
4637                 this.unselect(s, nodeInfo);
4638             }, this);
4639             return;
4640         }
4641         var node = this.getNode(nodeInfo);
4642         if(!node || !this.isSelected(node)){
4643             //Roo.log("not selected");
4644             return; // not selected.
4645         }
4646         // fireevent???
4647         var ns = [];
4648         Roo.each(this.selections, function(s) {
4649             if (s == node ) {
4650                 Roo.fly(node).removeClass(this.selectedClass);
4651
4652                 return;
4653             }
4654             ns.push(s);
4655         },this);
4656         
4657         this.selections= ns;
4658         this.fireEvent("selectionchange", this, this.selections);
4659     },
4660
4661     /**
4662      * Gets a template node.
4663      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4664      * @return {HTMLElement} The node or null if it wasn't found
4665      */
4666     getNode : function(nodeInfo){
4667         if(typeof nodeInfo == "string"){
4668             return document.getElementById(nodeInfo);
4669         }else if(typeof nodeInfo == "number"){
4670             return this.nodes[nodeInfo];
4671         }
4672         return nodeInfo;
4673     },
4674
4675     /**
4676      * Gets a range template nodes.
4677      * @param {Number} startIndex
4678      * @param {Number} endIndex
4679      * @return {Array} An array of nodes
4680      */
4681     getNodes : function(start, end){
4682         var ns = this.nodes;
4683         start = start || 0;
4684         end = typeof end == "undefined" ? ns.length - 1 : end;
4685         var nodes = [];
4686         if(start <= end){
4687             for(var i = start; i <= end; i++){
4688                 nodes.push(ns[i]);
4689             }
4690         } else{
4691             for(var i = start; i >= end; i--){
4692                 nodes.push(ns[i]);
4693             }
4694         }
4695         return nodes;
4696     },
4697
4698     /**
4699      * Finds the index of the passed node
4700      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4701      * @return {Number} The index of the node or -1
4702      */
4703     indexOf : function(node){
4704         node = this.getNode(node);
4705         if(typeof node.nodeIndex == "number"){
4706             return node.nodeIndex;
4707         }
4708         var ns = this.nodes;
4709         for(var i = 0, len = ns.length; i < len; i++){
4710             if(ns[i] == node){
4711                 return i;
4712             }
4713         }
4714         return -1;
4715     }
4716 });
4717 /*
4718  * Based on:
4719  * Ext JS Library 1.1.1
4720  * Copyright(c) 2006-2007, Ext JS, LLC.
4721  *
4722  * Originally Released Under LGPL - original licence link has changed is not relivant.
4723  *
4724  * Fork - LGPL
4725  * <script type="text/javascript">
4726  */
4727
4728 /**
4729  * @class Roo.JsonView
4730  * @extends Roo.View
4731  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4732 <pre><code>
4733 var view = new Roo.JsonView({
4734     container: "my-element",
4735     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4736     multiSelect: true, 
4737     jsonRoot: "data" 
4738 });
4739
4740 // listen for node click?
4741 view.on("click", function(vw, index, node, e){
4742     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4743 });
4744
4745 // direct load of JSON data
4746 view.load("foobar.php");
4747
4748 // Example from my blog list
4749 var tpl = new Roo.Template(
4750     '&lt;div class="entry"&gt;' +
4751     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4752     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4753     "&lt;/div&gt;&lt;hr /&gt;"
4754 );
4755
4756 var moreView = new Roo.JsonView({
4757     container :  "entry-list", 
4758     template : tpl,
4759     jsonRoot: "posts"
4760 });
4761 moreView.on("beforerender", this.sortEntries, this);
4762 moreView.load({
4763     url: "/blog/get-posts.php",
4764     params: "allposts=true",
4765     text: "Loading Blog Entries..."
4766 });
4767 </code></pre>
4768
4769 * Note: old code is supported with arguments : (container, template, config)
4770
4771
4772  * @constructor
4773  * Create a new JsonView
4774  * 
4775  * @param {Object} config The config object
4776  * 
4777  */
4778 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4779     
4780     
4781     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4782
4783     var um = this.el.getUpdateManager();
4784     um.setRenderer(this);
4785     um.on("update", this.onLoad, this);
4786     um.on("failure", this.onLoadException, this);
4787
4788     /**
4789      * @event beforerender
4790      * Fires before rendering of the downloaded JSON data.
4791      * @param {Roo.JsonView} this
4792      * @param {Object} data The JSON data loaded
4793      */
4794     /**
4795      * @event load
4796      * Fires when data is loaded.
4797      * @param {Roo.JsonView} this
4798      * @param {Object} data The JSON data loaded
4799      * @param {Object} response The raw Connect response object
4800      */
4801     /**
4802      * @event loadexception
4803      * Fires when loading fails.
4804      * @param {Roo.JsonView} this
4805      * @param {Object} response The raw Connect response object
4806      */
4807     this.addEvents({
4808         'beforerender' : true,
4809         'load' : true,
4810         'loadexception' : true
4811     });
4812 };
4813 Roo.extend(Roo.JsonView, Roo.View, {
4814     /**
4815      * @type {String} The root property in the loaded JSON object that contains the data
4816      */
4817     jsonRoot : "",
4818
4819     /**
4820      * Refreshes the view.
4821      */
4822     refresh : function(){
4823         this.clearSelections();
4824         this.el.update("");
4825         var html = [];
4826         var o = this.jsonData;
4827         if(o && o.length > 0){
4828             for(var i = 0, len = o.length; i < len; i++){
4829                 var data = this.prepareData(o[i], i, o);
4830                 html[html.length] = this.tpl.apply(data);
4831             }
4832         }else{
4833             html.push(this.emptyText);
4834         }
4835         this.el.update(html.join(""));
4836         this.nodes = this.el.dom.childNodes;
4837         this.updateIndexes(0);
4838     },
4839
4840     /**
4841      * 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.
4842      * @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:
4843      <pre><code>
4844      view.load({
4845          url: "your-url.php",
4846          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4847          callback: yourFunction,
4848          scope: yourObject, //(optional scope)
4849          discardUrl: false,
4850          nocache: false,
4851          text: "Loading...",
4852          timeout: 30,
4853          scripts: false
4854      });
4855      </code></pre>
4856      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4857      * 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.
4858      * @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}
4859      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4860      * @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.
4861      */
4862     load : function(){
4863         var um = this.el.getUpdateManager();
4864         um.update.apply(um, arguments);
4865     },
4866
4867     // note - render is a standard framework call...
4868     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4869     render : function(el, response){
4870         
4871         this.clearSelections();
4872         this.el.update("");
4873         var o;
4874         try{
4875             if (response != '') {
4876                 o = Roo.util.JSON.decode(response.responseText);
4877                 if(this.jsonRoot){
4878                     
4879                     o = o[this.jsonRoot];
4880                 }
4881             }
4882         } catch(e){
4883         }
4884         /**
4885          * The current JSON data or null
4886          */
4887         this.jsonData = o;
4888         this.beforeRender();
4889         this.refresh();
4890     },
4891
4892 /**
4893  * Get the number of records in the current JSON dataset
4894  * @return {Number}
4895  */
4896     getCount : function(){
4897         return this.jsonData ? this.jsonData.length : 0;
4898     },
4899
4900 /**
4901  * Returns the JSON object for the specified node(s)
4902  * @param {HTMLElement/Array} node The node or an array of nodes
4903  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4904  * you get the JSON object for the node
4905  */
4906     getNodeData : function(node){
4907         if(node instanceof Array){
4908             var data = [];
4909             for(var i = 0, len = node.length; i < len; i++){
4910                 data.push(this.getNodeData(node[i]));
4911             }
4912             return data;
4913         }
4914         return this.jsonData[this.indexOf(node)] || null;
4915     },
4916
4917     beforeRender : function(){
4918         this.snapshot = this.jsonData;
4919         if(this.sortInfo){
4920             this.sort.apply(this, this.sortInfo);
4921         }
4922         this.fireEvent("beforerender", this, this.jsonData);
4923     },
4924
4925     onLoad : function(el, o){
4926         this.fireEvent("load", this, this.jsonData, o);
4927     },
4928
4929     onLoadException : function(el, o){
4930         this.fireEvent("loadexception", this, o);
4931     },
4932
4933 /**
4934  * Filter the data by a specific property.
4935  * @param {String} property A property on your JSON objects
4936  * @param {String/RegExp} value Either string that the property values
4937  * should start with, or a RegExp to test against the property
4938  */
4939     filter : function(property, value){
4940         if(this.jsonData){
4941             var data = [];
4942             var ss = this.snapshot;
4943             if(typeof value == "string"){
4944                 var vlen = value.length;
4945                 if(vlen == 0){
4946                     this.clearFilter();
4947                     return;
4948                 }
4949                 value = value.toLowerCase();
4950                 for(var i = 0, len = ss.length; i < len; i++){
4951                     var o = ss[i];
4952                     if(o[property].substr(0, vlen).toLowerCase() == value){
4953                         data.push(o);
4954                     }
4955                 }
4956             } else if(value.exec){ // regex?
4957                 for(var i = 0, len = ss.length; i < len; i++){
4958                     var o = ss[i];
4959                     if(value.test(o[property])){
4960                         data.push(o);
4961                     }
4962                 }
4963             } else{
4964                 return;
4965             }
4966             this.jsonData = data;
4967             this.refresh();
4968         }
4969     },
4970
4971 /**
4972  * Filter by a function. The passed function will be called with each
4973  * object in the current dataset. If the function returns true the value is kept,
4974  * otherwise it is filtered.
4975  * @param {Function} fn
4976  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4977  */
4978     filterBy : function(fn, scope){
4979         if(this.jsonData){
4980             var data = [];
4981             var ss = this.snapshot;
4982             for(var i = 0, len = ss.length; i < len; i++){
4983                 var o = ss[i];
4984                 if(fn.call(scope || this, o)){
4985                     data.push(o);
4986                 }
4987             }
4988             this.jsonData = data;
4989             this.refresh();
4990         }
4991     },
4992
4993 /**
4994  * Clears the current filter.
4995  */
4996     clearFilter : function(){
4997         if(this.snapshot && this.jsonData != this.snapshot){
4998             this.jsonData = this.snapshot;
4999             this.refresh();
5000         }
5001     },
5002
5003
5004 /**
5005  * Sorts the data for this view and refreshes it.
5006  * @param {String} property A property on your JSON objects to sort on
5007  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
5008  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
5009  */
5010     sort : function(property, dir, sortType){
5011         this.sortInfo = Array.prototype.slice.call(arguments, 0);
5012         if(this.jsonData){
5013             var p = property;
5014             var dsc = dir && dir.toLowerCase() == "desc";
5015             var f = function(o1, o2){
5016                 var v1 = sortType ? sortType(o1[p]) : o1[p];
5017                 var v2 = sortType ? sortType(o2[p]) : o2[p];
5018                 ;
5019                 if(v1 < v2){
5020                     return dsc ? +1 : -1;
5021                 } else if(v1 > v2){
5022                     return dsc ? -1 : +1;
5023                 } else{
5024                     return 0;
5025                 }
5026             };
5027             this.jsonData.sort(f);
5028             this.refresh();
5029             if(this.jsonData != this.snapshot){
5030                 this.snapshot.sort(f);
5031             }
5032         }
5033     }
5034 });/*
5035  * Based on:
5036  * Ext JS Library 1.1.1
5037  * Copyright(c) 2006-2007, Ext JS, LLC.
5038  *
5039  * Originally Released Under LGPL - original licence link has changed is not relivant.
5040  *
5041  * Fork - LGPL
5042  * <script type="text/javascript">
5043  */
5044  
5045
5046 /**
5047  * @class Roo.ColorPalette
5048  * @extends Roo.Component
5049  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
5050  * Here's an example of typical usage:
5051  * <pre><code>
5052 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
5053 cp.render('my-div');
5054
5055 cp.on('select', function(palette, selColor){
5056     // do something with selColor
5057 });
5058 </code></pre>
5059  * @constructor
5060  * Create a new ColorPalette
5061  * @param {Object} config The config object
5062  */
5063 Roo.ColorPalette = function(config){
5064     Roo.ColorPalette.superclass.constructor.call(this, config);
5065     this.addEvents({
5066         /**
5067              * @event select
5068              * Fires when a color is selected
5069              * @param {ColorPalette} this
5070              * @param {String} color The 6-digit color hex code (without the # symbol)
5071              */
5072         select: true
5073     });
5074
5075     if(this.handler){
5076         this.on("select", this.handler, this.scope, true);
5077     }
5078 };
5079 Roo.extend(Roo.ColorPalette, Roo.Component, {
5080     /**
5081      * @cfg {String} itemCls
5082      * The CSS class to apply to the containing element (defaults to "x-color-palette")
5083      */
5084     itemCls : "x-color-palette",
5085     /**
5086      * @cfg {String} value
5087      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
5088      * the hex codes are case-sensitive.
5089      */
5090     value : null,
5091     clickEvent:'click',
5092     // private
5093     ctype: "Roo.ColorPalette",
5094
5095     /**
5096      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
5097      */
5098     allowReselect : false,
5099
5100     /**
5101      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
5102      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
5103      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
5104      * of colors with the width setting until the box is symmetrical.</p>
5105      * <p>You can override individual colors if needed:</p>
5106      * <pre><code>
5107 var cp = new Roo.ColorPalette();
5108 cp.colors[0] = "FF0000";  // change the first box to red
5109 </code></pre>
5110
5111 Or you can provide a custom array of your own for complete control:
5112 <pre><code>
5113 var cp = new Roo.ColorPalette();
5114 cp.colors = ["000000", "993300", "333300"];
5115 </code></pre>
5116      * @type Array
5117      */
5118     colors : [
5119         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
5120         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
5121         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
5122         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
5123         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
5124     ],
5125
5126     // private
5127     onRender : function(container, position){
5128         var t = new Roo.MasterTemplate(
5129             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
5130         );
5131         var c = this.colors;
5132         for(var i = 0, len = c.length; i < len; i++){
5133             t.add([c[i]]);
5134         }
5135         var el = document.createElement("div");
5136         el.className = this.itemCls;
5137         t.overwrite(el);
5138         container.dom.insertBefore(el, position);
5139         this.el = Roo.get(el);
5140         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
5141         if(this.clickEvent != 'click'){
5142             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
5143         }
5144     },
5145
5146     // private
5147     afterRender : function(){
5148         Roo.ColorPalette.superclass.afterRender.call(this);
5149         if(this.value){
5150             var s = this.value;
5151             this.value = null;
5152             this.select(s);
5153         }
5154     },
5155
5156     // private
5157     handleClick : function(e, t){
5158         e.preventDefault();
5159         if(!this.disabled){
5160             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
5161             this.select(c.toUpperCase());
5162         }
5163     },
5164
5165     /**
5166      * Selects the specified color in the palette (fires the select event)
5167      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
5168      */
5169     select : function(color){
5170         color = color.replace("#", "");
5171         if(color != this.value || this.allowReselect){
5172             var el = this.el;
5173             if(this.value){
5174                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
5175             }
5176             el.child("a.color-"+color).addClass("x-color-palette-sel");
5177             this.value = color;
5178             this.fireEvent("select", this, color);
5179         }
5180     }
5181 });/*
5182  * Based on:
5183  * Ext JS Library 1.1.1
5184  * Copyright(c) 2006-2007, Ext JS, LLC.
5185  *
5186  * Originally Released Under LGPL - original licence link has changed is not relivant.
5187  *
5188  * Fork - LGPL
5189  * <script type="text/javascript">
5190  */
5191  
5192 /**
5193  * @class Roo.DatePicker
5194  * @extends Roo.Component
5195  * Simple date picker class.
5196  * @constructor
5197  * Create a new DatePicker
5198  * @param {Object} config The config object
5199  */
5200 Roo.DatePicker = function(config){
5201     Roo.DatePicker.superclass.constructor.call(this, config);
5202
5203     this.value = config && config.value ?
5204                  config.value.clearTime() : new Date().clearTime();
5205
5206     this.addEvents({
5207         /**
5208              * @event select
5209              * Fires when a date is selected
5210              * @param {DatePicker} this
5211              * @param {Date} date The selected date
5212              */
5213         'select': true,
5214         /**
5215              * @event monthchange
5216              * Fires when the displayed month changes 
5217              * @param {DatePicker} this
5218              * @param {Date} date The selected month
5219              */
5220         'monthchange': true
5221     });
5222
5223     if(this.handler){
5224         this.on("select", this.handler,  this.scope || this);
5225     }
5226     // build the disabledDatesRE
5227     if(!this.disabledDatesRE && this.disabledDates){
5228         var dd = this.disabledDates;
5229         var re = "(?:";
5230         for(var i = 0; i < dd.length; i++){
5231             re += dd[i];
5232             if(i != dd.length-1) {
5233                 re += "|";
5234             }
5235         }
5236         this.disabledDatesRE = new RegExp(re + ")");
5237     }
5238 };
5239
5240 Roo.extend(Roo.DatePicker, Roo.Component, {
5241     /**
5242      * @cfg {String} todayText
5243      * The text to display on the button that selects the current date (defaults to "Today")
5244      */
5245     todayText : "Today",
5246     /**
5247      * @cfg {String} okText
5248      * The text to display on the ok button
5249      */
5250     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
5251     /**
5252      * @cfg {String} cancelText
5253      * The text to display on the cancel button
5254      */
5255     cancelText : "Cancel",
5256     /**
5257      * @cfg {String} todayTip
5258      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
5259      */
5260     todayTip : "{0} (Spacebar)",
5261     /**
5262      * @cfg {Date} minDate
5263      * Minimum allowable date (JavaScript date object, defaults to null)
5264      */
5265     minDate : null,
5266     /**
5267      * @cfg {Date} maxDate
5268      * Maximum allowable date (JavaScript date object, defaults to null)
5269      */
5270     maxDate : null,
5271     /**
5272      * @cfg {String} minText
5273      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
5274      */
5275     minText : "This date is before the minimum date",
5276     /**
5277      * @cfg {String} maxText
5278      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
5279      */
5280     maxText : "This date is after the maximum date",
5281     /**
5282      * @cfg {String} format
5283      * The default date format string which can be overriden for localization support.  The format must be
5284      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
5285      */
5286     format : "m/d/y",
5287     /**
5288      * @cfg {Array} disabledDays
5289      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
5290      */
5291     disabledDays : null,
5292     /**
5293      * @cfg {String} disabledDaysText
5294      * The tooltip to display when the date falls on a disabled day (defaults to "")
5295      */
5296     disabledDaysText : "",
5297     /**
5298      * @cfg {RegExp} disabledDatesRE
5299      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
5300      */
5301     disabledDatesRE : null,
5302     /**
5303      * @cfg {String} disabledDatesText
5304      * The tooltip text to display when the date falls on a disabled date (defaults to "")
5305      */
5306     disabledDatesText : "",
5307     /**
5308      * @cfg {Boolean} constrainToViewport
5309      * True to constrain the date picker to the viewport (defaults to true)
5310      */
5311     constrainToViewport : true,
5312     /**
5313      * @cfg {Array} monthNames
5314      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
5315      */
5316     monthNames : Date.monthNames,
5317     /**
5318      * @cfg {Array} dayNames
5319      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
5320      */
5321     dayNames : Date.dayNames,
5322     /**
5323      * @cfg {String} nextText
5324      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
5325      */
5326     nextText: 'Next Month (Control+Right)',
5327     /**
5328      * @cfg {String} prevText
5329      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
5330      */
5331     prevText: 'Previous Month (Control+Left)',
5332     /**
5333      * @cfg {String} monthYearText
5334      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
5335      */
5336     monthYearText: 'Choose a month (Control+Up/Down to move years)',
5337     /**
5338      * @cfg {Number} startDay
5339      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
5340      */
5341     startDay : 0,
5342     /**
5343      * @cfg {Bool} showClear
5344      * Show a clear button (usefull for date form elements that can be blank.)
5345      */
5346     
5347     showClear: false,
5348     
5349     /**
5350      * Sets the value of the date field
5351      * @param {Date} value The date to set
5352      */
5353     setValue : function(value){
5354         var old = this.value;
5355         
5356         if (typeof(value) == 'string') {
5357          
5358             value = Date.parseDate(value, this.format);
5359         }
5360         if (!value) {
5361             value = new Date();
5362         }
5363         
5364         this.value = value.clearTime(true);
5365         if(this.el){
5366             this.update(this.value);
5367         }
5368     },
5369
5370     /**
5371      * Gets the current selected value of the date field
5372      * @return {Date} The selected date
5373      */
5374     getValue : function(){
5375         return this.value;
5376     },
5377
5378     // private
5379     focus : function(){
5380         if(this.el){
5381             this.update(this.activeDate);
5382         }
5383     },
5384
5385     // privateval
5386     onRender : function(container, position){
5387         
5388         var m = [
5389              '<table cellspacing="0">',
5390                 '<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>',
5391                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
5392         var dn = this.dayNames;
5393         for(var i = 0; i < 7; i++){
5394             var d = this.startDay+i;
5395             if(d > 6){
5396                 d = d-7;
5397             }
5398             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5399         }
5400         m[m.length] = "</tr></thead><tbody><tr>";
5401         for(var i = 0; i < 42; i++) {
5402             if(i % 7 == 0 && i != 0){
5403                 m[m.length] = "</tr><tr>";
5404             }
5405             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5406         }
5407         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5408             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5409
5410         var el = document.createElement("div");
5411         el.className = "x-date-picker";
5412         el.innerHTML = m.join("");
5413
5414         container.dom.insertBefore(el, position);
5415
5416         this.el = Roo.get(el);
5417         this.eventEl = Roo.get(el.firstChild);
5418
5419         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5420             handler: this.showPrevMonth,
5421             scope: this,
5422             preventDefault:true,
5423             stopDefault:true
5424         });
5425
5426         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5427             handler: this.showNextMonth,
5428             scope: this,
5429             preventDefault:true,
5430             stopDefault:true
5431         });
5432
5433         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5434
5435         this.monthPicker = this.el.down('div.x-date-mp');
5436         this.monthPicker.enableDisplayMode('block');
5437         
5438         var kn = new Roo.KeyNav(this.eventEl, {
5439             "left" : function(e){
5440                 e.ctrlKey ?
5441                     this.showPrevMonth() :
5442                     this.update(this.activeDate.add("d", -1));
5443             },
5444
5445             "right" : function(e){
5446                 e.ctrlKey ?
5447                     this.showNextMonth() :
5448                     this.update(this.activeDate.add("d", 1));
5449             },
5450
5451             "up" : function(e){
5452                 e.ctrlKey ?
5453                     this.showNextYear() :
5454                     this.update(this.activeDate.add("d", -7));
5455             },
5456
5457             "down" : function(e){
5458                 e.ctrlKey ?
5459                     this.showPrevYear() :
5460                     this.update(this.activeDate.add("d", 7));
5461             },
5462
5463             "pageUp" : function(e){
5464                 this.showNextMonth();
5465             },
5466
5467             "pageDown" : function(e){
5468                 this.showPrevMonth();
5469             },
5470
5471             "enter" : function(e){
5472                 e.stopPropagation();
5473                 return true;
5474             },
5475
5476             scope : this
5477         });
5478
5479         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5480
5481         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5482
5483         this.el.unselectable();
5484         
5485         this.cells = this.el.select("table.x-date-inner tbody td");
5486         this.textNodes = this.el.query("table.x-date-inner tbody span");
5487
5488         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5489             text: "&#160;",
5490             tooltip: this.monthYearText
5491         });
5492
5493         this.mbtn.on('click', this.showMonthPicker, this);
5494         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5495
5496
5497         var today = (new Date()).dateFormat(this.format);
5498         
5499         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5500         if (this.showClear) {
5501             baseTb.add( new Roo.Toolbar.Fill());
5502         }
5503         baseTb.add({
5504             text: String.format(this.todayText, today),
5505             tooltip: String.format(this.todayTip, today),
5506             handler: this.selectToday,
5507             scope: this
5508         });
5509         
5510         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5511             
5512         //});
5513         if (this.showClear) {
5514             
5515             baseTb.add( new Roo.Toolbar.Fill());
5516             baseTb.add({
5517                 text: '&#160;',
5518                 cls: 'x-btn-icon x-btn-clear',
5519                 handler: function() {
5520                     //this.value = '';
5521                     this.fireEvent("select", this, '');
5522                 },
5523                 scope: this
5524             });
5525         }
5526         
5527         
5528         if(Roo.isIE){
5529             this.el.repaint();
5530         }
5531         this.update(this.value);
5532     },
5533
5534     createMonthPicker : function(){
5535         if(!this.monthPicker.dom.firstChild){
5536             var buf = ['<table border="0" cellspacing="0">'];
5537             for(var i = 0; i < 6; i++){
5538                 buf.push(
5539                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5540                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5541                     i == 0 ?
5542                     '<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>' :
5543                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5544                 );
5545             }
5546             buf.push(
5547                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5548                     this.okText,
5549                     '</button><button type="button" class="x-date-mp-cancel">',
5550                     this.cancelText,
5551                     '</button></td></tr>',
5552                 '</table>'
5553             );
5554             this.monthPicker.update(buf.join(''));
5555             this.monthPicker.on('click', this.onMonthClick, this);
5556             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5557
5558             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5559             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5560
5561             this.mpMonths.each(function(m, a, i){
5562                 i += 1;
5563                 if((i%2) == 0){
5564                     m.dom.xmonth = 5 + Math.round(i * .5);
5565                 }else{
5566                     m.dom.xmonth = Math.round((i-1) * .5);
5567                 }
5568             });
5569         }
5570     },
5571
5572     showMonthPicker : function(){
5573         this.createMonthPicker();
5574         var size = this.el.getSize();
5575         this.monthPicker.setSize(size);
5576         this.monthPicker.child('table').setSize(size);
5577
5578         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5579         this.updateMPMonth(this.mpSelMonth);
5580         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5581         this.updateMPYear(this.mpSelYear);
5582
5583         this.monthPicker.slideIn('t', {duration:.2});
5584     },
5585
5586     updateMPYear : function(y){
5587         this.mpyear = y;
5588         var ys = this.mpYears.elements;
5589         for(var i = 1; i <= 10; i++){
5590             var td = ys[i-1], y2;
5591             if((i%2) == 0){
5592                 y2 = y + Math.round(i * .5);
5593                 td.firstChild.innerHTML = y2;
5594                 td.xyear = y2;
5595             }else{
5596                 y2 = y - (5-Math.round(i * .5));
5597                 td.firstChild.innerHTML = y2;
5598                 td.xyear = y2;
5599             }
5600             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5601         }
5602     },
5603
5604     updateMPMonth : function(sm){
5605         this.mpMonths.each(function(m, a, i){
5606             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5607         });
5608     },
5609
5610     selectMPMonth: function(m){
5611         
5612     },
5613
5614     onMonthClick : function(e, t){
5615         e.stopEvent();
5616         var el = new Roo.Element(t), pn;
5617         if(el.is('button.x-date-mp-cancel')){
5618             this.hideMonthPicker();
5619         }
5620         else if(el.is('button.x-date-mp-ok')){
5621             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5622             this.hideMonthPicker();
5623         }
5624         else if(pn = el.up('td.x-date-mp-month', 2)){
5625             this.mpMonths.removeClass('x-date-mp-sel');
5626             pn.addClass('x-date-mp-sel');
5627             this.mpSelMonth = pn.dom.xmonth;
5628         }
5629         else if(pn = el.up('td.x-date-mp-year', 2)){
5630             this.mpYears.removeClass('x-date-mp-sel');
5631             pn.addClass('x-date-mp-sel');
5632             this.mpSelYear = pn.dom.xyear;
5633         }
5634         else if(el.is('a.x-date-mp-prev')){
5635             this.updateMPYear(this.mpyear-10);
5636         }
5637         else if(el.is('a.x-date-mp-next')){
5638             this.updateMPYear(this.mpyear+10);
5639         }
5640     },
5641
5642     onMonthDblClick : function(e, t){
5643         e.stopEvent();
5644         var el = new Roo.Element(t), pn;
5645         if(pn = el.up('td.x-date-mp-month', 2)){
5646             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5647             this.hideMonthPicker();
5648         }
5649         else if(pn = el.up('td.x-date-mp-year', 2)){
5650             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5651             this.hideMonthPicker();
5652         }
5653     },
5654
5655     hideMonthPicker : function(disableAnim){
5656         if(this.monthPicker){
5657             if(disableAnim === true){
5658                 this.monthPicker.hide();
5659             }else{
5660                 this.monthPicker.slideOut('t', {duration:.2});
5661             }
5662         }
5663     },
5664
5665     // private
5666     showPrevMonth : function(e){
5667         this.update(this.activeDate.add("mo", -1));
5668     },
5669
5670     // private
5671     showNextMonth : function(e){
5672         this.update(this.activeDate.add("mo", 1));
5673     },
5674
5675     // private
5676     showPrevYear : function(){
5677         this.update(this.activeDate.add("y", -1));
5678     },
5679
5680     // private
5681     showNextYear : function(){
5682         this.update(this.activeDate.add("y", 1));
5683     },
5684
5685     // private
5686     handleMouseWheel : function(e){
5687         var delta = e.getWheelDelta();
5688         if(delta > 0){
5689             this.showPrevMonth();
5690             e.stopEvent();
5691         } else if(delta < 0){
5692             this.showNextMonth();
5693             e.stopEvent();
5694         }
5695     },
5696
5697     // private
5698     handleDateClick : function(e, t){
5699         e.stopEvent();
5700         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5701             this.setValue(new Date(t.dateValue));
5702             this.fireEvent("select", this, this.value);
5703         }
5704     },
5705
5706     // private
5707     selectToday : function(){
5708         this.setValue(new Date().clearTime());
5709         this.fireEvent("select", this, this.value);
5710     },
5711
5712     // private
5713     update : function(date)
5714     {
5715         var vd = this.activeDate;
5716         this.activeDate = date;
5717         if(vd && this.el){
5718             var t = date.getTime();
5719             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5720                 this.cells.removeClass("x-date-selected");
5721                 this.cells.each(function(c){
5722                    if(c.dom.firstChild.dateValue == t){
5723                        c.addClass("x-date-selected");
5724                        setTimeout(function(){
5725                             try{c.dom.firstChild.focus();}catch(e){}
5726                        }, 50);
5727                        return false;
5728                    }
5729                 });
5730                 return;
5731             }
5732         }
5733         
5734         var days = date.getDaysInMonth();
5735         var firstOfMonth = date.getFirstDateOfMonth();
5736         var startingPos = firstOfMonth.getDay()-this.startDay;
5737
5738         if(startingPos <= this.startDay){
5739             startingPos += 7;
5740         }
5741
5742         var pm = date.add("mo", -1);
5743         var prevStart = pm.getDaysInMonth()-startingPos;
5744
5745         var cells = this.cells.elements;
5746         var textEls = this.textNodes;
5747         days += startingPos;
5748
5749         // convert everything to numbers so it's fast
5750         var day = 86400000;
5751         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5752         var today = new Date().clearTime().getTime();
5753         var sel = date.clearTime().getTime();
5754         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5755         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5756         var ddMatch = this.disabledDatesRE;
5757         var ddText = this.disabledDatesText;
5758         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5759         var ddaysText = this.disabledDaysText;
5760         var format = this.format;
5761
5762         var setCellClass = function(cal, cell){
5763             cell.title = "";
5764             var t = d.getTime();
5765             cell.firstChild.dateValue = t;
5766             if(t == today){
5767                 cell.className += " x-date-today";
5768                 cell.title = cal.todayText;
5769             }
5770             if(t == sel){
5771                 cell.className += " x-date-selected";
5772                 setTimeout(function(){
5773                     try{cell.firstChild.focus();}catch(e){}
5774                 }, 50);
5775             }
5776             // disabling
5777             if(t < min) {
5778                 cell.className = " x-date-disabled";
5779                 cell.title = cal.minText;
5780                 return;
5781             }
5782             if(t > max) {
5783                 cell.className = " x-date-disabled";
5784                 cell.title = cal.maxText;
5785                 return;
5786             }
5787             if(ddays){
5788                 if(ddays.indexOf(d.getDay()) != -1){
5789                     cell.title = ddaysText;
5790                     cell.className = " x-date-disabled";
5791                 }
5792             }
5793             if(ddMatch && format){
5794                 var fvalue = d.dateFormat(format);
5795                 if(ddMatch.test(fvalue)){
5796                     cell.title = ddText.replace("%0", fvalue);
5797                     cell.className = " x-date-disabled";
5798                 }
5799             }
5800         };
5801
5802         var i = 0;
5803         for(; i < startingPos; i++) {
5804             textEls[i].innerHTML = (++prevStart);
5805             d.setDate(d.getDate()+1);
5806             cells[i].className = "x-date-prevday";
5807             setCellClass(this, cells[i]);
5808         }
5809         for(; i < days; i++){
5810             intDay = i - startingPos + 1;
5811             textEls[i].innerHTML = (intDay);
5812             d.setDate(d.getDate()+1);
5813             cells[i].className = "x-date-active";
5814             setCellClass(this, cells[i]);
5815         }
5816         var extraDays = 0;
5817         for(; i < 42; i++) {
5818              textEls[i].innerHTML = (++extraDays);
5819              d.setDate(d.getDate()+1);
5820              cells[i].className = "x-date-nextday";
5821              setCellClass(this, cells[i]);
5822         }
5823
5824         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5825         this.fireEvent('monthchange', this, date);
5826         
5827         if(!this.internalRender){
5828             var main = this.el.dom.firstChild;
5829             var w = main.offsetWidth;
5830             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5831             Roo.fly(main).setWidth(w);
5832             this.internalRender = true;
5833             // opera does not respect the auto grow header center column
5834             // then, after it gets a width opera refuses to recalculate
5835             // without a second pass
5836             if(Roo.isOpera && !this.secondPass){
5837                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5838                 this.secondPass = true;
5839                 this.update.defer(10, this, [date]);
5840             }
5841         }
5842         
5843         
5844     }
5845 });        /*
5846  * Based on:
5847  * Ext JS Library 1.1.1
5848  * Copyright(c) 2006-2007, Ext JS, LLC.
5849  *
5850  * Originally Released Under LGPL - original licence link has changed is not relivant.
5851  *
5852  * Fork - LGPL
5853  * <script type="text/javascript">
5854  */
5855 /**
5856  * @class Roo.TabPanel
5857  * @extends Roo.util.Observable
5858  * A lightweight tab container.
5859  * <br><br>
5860  * Usage:
5861  * <pre><code>
5862 // basic tabs 1, built from existing content
5863 var tabs = new Roo.TabPanel("tabs1");
5864 tabs.addTab("script", "View Script");
5865 tabs.addTab("markup", "View Markup");
5866 tabs.activate("script");
5867
5868 // more advanced tabs, built from javascript
5869 var jtabs = new Roo.TabPanel("jtabs");
5870 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5871
5872 // set up the UpdateManager
5873 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5874 var updater = tab2.getUpdateManager();
5875 updater.setDefaultUrl("ajax1.htm");
5876 tab2.on('activate', updater.refresh, updater, true);
5877
5878 // Use setUrl for Ajax loading
5879 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5880 tab3.setUrl("ajax2.htm", null, true);
5881
5882 // Disabled tab
5883 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5884 tab4.disable();
5885
5886 jtabs.activate("jtabs-1");
5887  * </code></pre>
5888  * @constructor
5889  * Create a new TabPanel.
5890  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5891  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5892  */
5893 Roo.TabPanel = function(container, config){
5894     /**
5895     * The container element for this TabPanel.
5896     * @type Roo.Element
5897     */
5898     this.el = Roo.get(container, true);
5899     if(config){
5900         if(typeof config == "boolean"){
5901             this.tabPosition = config ? "bottom" : "top";
5902         }else{
5903             Roo.apply(this, config);
5904         }
5905     }
5906     if(this.tabPosition == "bottom"){
5907         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5908         this.el.addClass("x-tabs-bottom");
5909     }
5910     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5911     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5912     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5913     if(Roo.isIE){
5914         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5915     }
5916     if(this.tabPosition != "bottom"){
5917         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5918          * @type Roo.Element
5919          */
5920         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5921         this.el.addClass("x-tabs-top");
5922     }
5923     this.items = [];
5924
5925     this.bodyEl.setStyle("position", "relative");
5926
5927     this.active = null;
5928     this.activateDelegate = this.activate.createDelegate(this);
5929
5930     this.addEvents({
5931         /**
5932          * @event tabchange
5933          * Fires when the active tab changes
5934          * @param {Roo.TabPanel} this
5935          * @param {Roo.TabPanelItem} activePanel The new active tab
5936          */
5937         "tabchange": true,
5938         /**
5939          * @event beforetabchange
5940          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5941          * @param {Roo.TabPanel} this
5942          * @param {Object} e Set cancel to true on this object to cancel the tab change
5943          * @param {Roo.TabPanelItem} tab The tab being changed to
5944          */
5945         "beforetabchange" : true
5946     });
5947
5948     Roo.EventManager.onWindowResize(this.onResize, this);
5949     this.cpad = this.el.getPadding("lr");
5950     this.hiddenCount = 0;
5951
5952
5953     // toolbar on the tabbar support...
5954     if (this.toolbar) {
5955         var tcfg = this.toolbar;
5956         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
5957         this.toolbar = new Roo.Toolbar(tcfg);
5958         if (Roo.isSafari) {
5959             var tbl = tcfg.container.child('table', true);
5960             tbl.setAttribute('width', '100%');
5961         }
5962         
5963     }
5964    
5965
5966
5967     Roo.TabPanel.superclass.constructor.call(this);
5968 };
5969
5970 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5971     /*
5972      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5973      */
5974     tabPosition : "top",
5975     /*
5976      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5977      */
5978     currentTabWidth : 0,
5979     /*
5980      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5981      */
5982     minTabWidth : 40,
5983     /*
5984      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5985      */
5986     maxTabWidth : 250,
5987     /*
5988      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5989      */
5990     preferredTabWidth : 175,
5991     /*
5992      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
5993      */
5994     resizeTabs : false,
5995     /*
5996      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
5997      */
5998     monitorResize : true,
5999     /*
6000      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
6001      */
6002     toolbar : false,
6003
6004     /**
6005      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
6006      * @param {String} id The id of the div to use <b>or create</b>
6007      * @param {String} text The text for the tab
6008      * @param {String} content (optional) Content to put in the TabPanelItem body
6009      * @param {Boolean} closable (optional) True to create a close icon on the tab
6010      * @return {Roo.TabPanelItem} The created TabPanelItem
6011      */
6012     addTab : function(id, text, content, closable){
6013         var item = new Roo.TabPanelItem(this, id, text, closable);
6014         this.addTabItem(item);
6015         if(content){
6016             item.setContent(content);
6017         }
6018         return item;
6019     },
6020
6021     /**
6022      * Returns the {@link Roo.TabPanelItem} with the specified id/index
6023      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
6024      * @return {Roo.TabPanelItem}
6025      */
6026     getTab : function(id){
6027         return this.items[id];
6028     },
6029
6030     /**
6031      * Hides the {@link Roo.TabPanelItem} with the specified id/index
6032      * @param {String/Number} id The id or index of the TabPanelItem to hide.
6033      */
6034     hideTab : function(id){
6035         var t = this.items[id];
6036         if(!t.isHidden()){
6037            t.setHidden(true);
6038            this.hiddenCount++;
6039            this.autoSizeTabs();
6040         }
6041     },
6042
6043     /**
6044      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
6045      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
6046      */
6047     unhideTab : function(id){
6048         var t = this.items[id];
6049         if(t.isHidden()){
6050            t.setHidden(false);
6051            this.hiddenCount--;
6052            this.autoSizeTabs();
6053         }
6054     },
6055
6056     /**
6057      * Adds an existing {@link Roo.TabPanelItem}.
6058      * @param {Roo.TabPanelItem} item The TabPanelItem to add
6059      */
6060     addTabItem : function(item){
6061         this.items[item.id] = item;
6062         this.items.push(item);
6063         if(this.resizeTabs){
6064            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
6065            this.autoSizeTabs();
6066         }else{
6067             item.autoSize();
6068         }
6069     },
6070
6071     /**
6072      * Removes a {@link Roo.TabPanelItem}.
6073      * @param {String/Number} id The id or index of the TabPanelItem to remove.
6074      */
6075     removeTab : function(id){
6076         var items = this.items;
6077         var tab = items[id];
6078         if(!tab) { return; }
6079         var index = items.indexOf(tab);
6080         if(this.active == tab && items.length > 1){
6081             var newTab = this.getNextAvailable(index);
6082             if(newTab) {
6083                 newTab.activate();
6084             }
6085         }
6086         this.stripEl.dom.removeChild(tab.pnode.dom);
6087         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
6088             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
6089         }
6090         items.splice(index, 1);
6091         delete this.items[tab.id];
6092         tab.fireEvent("close", tab);
6093         tab.purgeListeners();
6094         this.autoSizeTabs();
6095     },
6096
6097     getNextAvailable : function(start){
6098         var items = this.items;
6099         var index = start;
6100         // look for a next tab that will slide over to
6101         // replace the one being removed
6102         while(index < items.length){
6103             var item = items[++index];
6104             if(item && !item.isHidden()){
6105                 return item;
6106             }
6107         }
6108         // if one isn't found select the previous tab (on the left)
6109         index = start;
6110         while(index >= 0){
6111             var item = items[--index];
6112             if(item && !item.isHidden()){
6113                 return item;
6114             }
6115         }
6116         return null;
6117     },
6118
6119     /**
6120      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
6121      * @param {String/Number} id The id or index of the TabPanelItem to disable.
6122      */
6123     disableTab : function(id){
6124         var tab = this.items[id];
6125         if(tab && this.active != tab){
6126             tab.disable();
6127         }
6128     },
6129
6130     /**
6131      * Enables a {@link Roo.TabPanelItem} that is disabled.
6132      * @param {String/Number} id The id or index of the TabPanelItem to enable.
6133      */
6134     enableTab : function(id){
6135         var tab = this.items[id];
6136         tab.enable();
6137     },
6138
6139     /**
6140      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
6141      * @param {String/Number} id The id or index of the TabPanelItem to activate.
6142      * @return {Roo.TabPanelItem} The TabPanelItem.
6143      */
6144     activate : function(id){
6145         var tab = this.items[id];
6146         if(!tab){
6147             return null;
6148         }
6149         if(tab == this.active || tab.disabled){
6150             return tab;
6151         }
6152         var e = {};
6153         this.fireEvent("beforetabchange", this, e, tab);
6154         if(e.cancel !== true && !tab.disabled){
6155             if(this.active){
6156                 this.active.hide();
6157             }
6158             this.active = this.items[id];
6159             this.active.show();
6160             this.fireEvent("tabchange", this, this.active);
6161         }
6162         return tab;
6163     },
6164
6165     /**
6166      * Gets the active {@link Roo.TabPanelItem}.
6167      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
6168      */
6169     getActiveTab : function(){
6170         return this.active;
6171     },
6172
6173     /**
6174      * Updates the tab body element to fit the height of the container element
6175      * for overflow scrolling
6176      * @param {Number} targetHeight (optional) Override the starting height from the elements height
6177      */
6178     syncHeight : function(targetHeight){
6179         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
6180         var bm = this.bodyEl.getMargins();
6181         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
6182         this.bodyEl.setHeight(newHeight);
6183         return newHeight;
6184     },
6185
6186     onResize : function(){
6187         if(this.monitorResize){
6188             this.autoSizeTabs();
6189         }
6190     },
6191
6192     /**
6193      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
6194      */
6195     beginUpdate : function(){
6196         this.updating = true;
6197     },
6198
6199     /**
6200      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
6201      */
6202     endUpdate : function(){
6203         this.updating = false;
6204         this.autoSizeTabs();
6205     },
6206
6207     /**
6208      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
6209      */
6210     autoSizeTabs : function(){
6211         var count = this.items.length;
6212         var vcount = count - this.hiddenCount;
6213         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
6214             return;
6215         }
6216         var w = Math.max(this.el.getWidth() - this.cpad, 10);
6217         var availWidth = Math.floor(w / vcount);
6218         var b = this.stripBody;
6219         if(b.getWidth() > w){
6220             var tabs = this.items;
6221             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
6222             if(availWidth < this.minTabWidth){
6223                 /*if(!this.sleft){    // incomplete scrolling code
6224                     this.createScrollButtons();
6225                 }
6226                 this.showScroll();
6227                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
6228             }
6229         }else{
6230             if(this.currentTabWidth < this.preferredTabWidth){
6231                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
6232             }
6233         }
6234     },
6235
6236     /**
6237      * Returns the number of tabs in this TabPanel.
6238      * @return {Number}
6239      */
6240      getCount : function(){
6241          return this.items.length;
6242      },
6243
6244     /**
6245      * Resizes all the tabs to the passed width
6246      * @param {Number} The new width
6247      */
6248     setTabWidth : function(width){
6249         this.currentTabWidth = width;
6250         for(var i = 0, len = this.items.length; i < len; i++) {
6251                 if(!this.items[i].isHidden()) {
6252                 this.items[i].setWidth(width);
6253             }
6254         }
6255     },
6256
6257     /**
6258      * Destroys this TabPanel
6259      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
6260      */
6261     destroy : function(removeEl){
6262         Roo.EventManager.removeResizeListener(this.onResize, this);
6263         for(var i = 0, len = this.items.length; i < len; i++){
6264             this.items[i].purgeListeners();
6265         }
6266         if(removeEl === true){
6267             this.el.update("");
6268             this.el.remove();
6269         }
6270     }
6271 });
6272
6273 /**
6274  * @class Roo.TabPanelItem
6275  * @extends Roo.util.Observable
6276  * Represents an individual item (tab plus body) in a TabPanel.
6277  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
6278  * @param {String} id The id of this TabPanelItem
6279  * @param {String} text The text for the tab of this TabPanelItem
6280  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
6281  */
6282 Roo.TabPanelItem = function(tabPanel, id, text, closable){
6283     /**
6284      * The {@link Roo.TabPanel} this TabPanelItem belongs to
6285      * @type Roo.TabPanel
6286      */
6287     this.tabPanel = tabPanel;
6288     /**
6289      * The id for this TabPanelItem
6290      * @type String
6291      */
6292     this.id = id;
6293     /** @private */
6294     this.disabled = false;
6295     /** @private */
6296     this.text = text;
6297     /** @private */
6298     this.loaded = false;
6299     this.closable = closable;
6300
6301     /**
6302      * The body element for this TabPanelItem.
6303      * @type Roo.Element
6304      */
6305     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
6306     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
6307     this.bodyEl.setStyle("display", "block");
6308     this.bodyEl.setStyle("zoom", "1");
6309     this.hideAction();
6310
6311     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
6312     /** @private */
6313     this.el = Roo.get(els.el, true);
6314     this.inner = Roo.get(els.inner, true);
6315     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
6316     this.pnode = Roo.get(els.el.parentNode, true);
6317     this.el.on("mousedown", this.onTabMouseDown, this);
6318     this.el.on("click", this.onTabClick, this);
6319     /** @private */
6320     if(closable){
6321         var c = Roo.get(els.close, true);
6322         c.dom.title = this.closeText;
6323         c.addClassOnOver("close-over");
6324         c.on("click", this.closeClick, this);
6325      }
6326
6327     this.addEvents({
6328          /**
6329          * @event activate
6330          * Fires when this tab becomes the active tab.
6331          * @param {Roo.TabPanel} tabPanel The parent TabPanel
6332          * @param {Roo.TabPanelItem} this
6333          */
6334         "activate": true,
6335         /**
6336          * @event beforeclose
6337          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
6338          * @param {Roo.TabPanelItem} this
6339          * @param {Object} e Set cancel to true on this object to cancel the close.
6340          */
6341         "beforeclose": true,
6342         /**
6343          * @event close
6344          * Fires when this tab is closed.
6345          * @param {Roo.TabPanelItem} this
6346          */
6347          "close": true,
6348         /**
6349          * @event deactivate
6350          * Fires when this tab is no longer the active tab.
6351          * @param {Roo.TabPanel} tabPanel The parent TabPanel
6352          * @param {Roo.TabPanelItem} this
6353          */
6354          "deactivate" : true
6355     });
6356     this.hidden = false;
6357
6358     Roo.TabPanelItem.superclass.constructor.call(this);
6359 };
6360
6361 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
6362     purgeListeners : function(){
6363        Roo.util.Observable.prototype.purgeListeners.call(this);
6364        this.el.removeAllListeners();
6365     },
6366     /**
6367      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
6368      */
6369     show : function(){
6370         this.pnode.addClass("on");
6371         this.showAction();
6372         if(Roo.isOpera){
6373             this.tabPanel.stripWrap.repaint();
6374         }
6375         this.fireEvent("activate", this.tabPanel, this);
6376     },
6377
6378     /**
6379      * Returns true if this tab is the active tab.
6380      * @return {Boolean}
6381      */
6382     isActive : function(){
6383         return this.tabPanel.getActiveTab() == this;
6384     },
6385
6386     /**
6387      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
6388      */
6389     hide : function(){
6390         this.pnode.removeClass("on");
6391         this.hideAction();
6392         this.fireEvent("deactivate", this.tabPanel, this);
6393     },
6394
6395     hideAction : function(){
6396         this.bodyEl.hide();
6397         this.bodyEl.setStyle("position", "absolute");
6398         this.bodyEl.setLeft("-20000px");
6399         this.bodyEl.setTop("-20000px");
6400     },
6401
6402     showAction : function(){
6403         this.bodyEl.setStyle("position", "relative");
6404         this.bodyEl.setTop("");
6405         this.bodyEl.setLeft("");
6406         this.bodyEl.show();
6407     },
6408
6409     /**
6410      * Set the tooltip for the tab.
6411      * @param {String} tooltip The tab's tooltip
6412      */
6413     setTooltip : function(text){
6414         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6415             this.textEl.dom.qtip = text;
6416             this.textEl.dom.removeAttribute('title');
6417         }else{
6418             this.textEl.dom.title = text;
6419         }
6420     },
6421
6422     onTabClick : function(e){
6423         e.preventDefault();
6424         this.tabPanel.activate(this.id);
6425     },
6426
6427     onTabMouseDown : function(e){
6428         e.preventDefault();
6429         this.tabPanel.activate(this.id);
6430     },
6431
6432     getWidth : function(){
6433         return this.inner.getWidth();
6434     },
6435
6436     setWidth : function(width){
6437         var iwidth = width - this.pnode.getPadding("lr");
6438         this.inner.setWidth(iwidth);
6439         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6440         this.pnode.setWidth(width);
6441     },
6442
6443     /**
6444      * Show or hide the tab
6445      * @param {Boolean} hidden True to hide or false to show.
6446      */
6447     setHidden : function(hidden){
6448         this.hidden = hidden;
6449         this.pnode.setStyle("display", hidden ? "none" : "");
6450     },
6451
6452     /**
6453      * Returns true if this tab is "hidden"
6454      * @return {Boolean}
6455      */
6456     isHidden : function(){
6457         return this.hidden;
6458     },
6459
6460     /**
6461      * Returns the text for this tab
6462      * @return {String}
6463      */
6464     getText : function(){
6465         return this.text;
6466     },
6467
6468     autoSize : function(){
6469         //this.el.beginMeasure();
6470         this.textEl.setWidth(1);
6471         /*
6472          *  #2804 [new] Tabs in Roojs
6473          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6474          */
6475         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6476         //this.el.endMeasure();
6477     },
6478
6479     /**
6480      * Sets the text for the tab (Note: this also sets the tooltip text)
6481      * @param {String} text The tab's text and tooltip
6482      */
6483     setText : function(text){
6484         this.text = text;
6485         this.textEl.update(text);
6486         this.setTooltip(text);
6487         if(!this.tabPanel.resizeTabs){
6488             this.autoSize();
6489         }
6490     },
6491     /**
6492      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6493      */
6494     activate : function(){
6495         this.tabPanel.activate(this.id);
6496     },
6497
6498     /**
6499      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6500      */
6501     disable : function(){
6502         if(this.tabPanel.active != this){
6503             this.disabled = true;
6504             this.pnode.addClass("disabled");
6505         }
6506     },
6507
6508     /**
6509      * Enables this TabPanelItem if it was previously disabled.
6510      */
6511     enable : function(){
6512         this.disabled = false;
6513         this.pnode.removeClass("disabled");
6514     },
6515
6516     /**
6517      * Sets the content for this TabPanelItem.
6518      * @param {String} content The content
6519      * @param {Boolean} loadScripts true to look for and load scripts
6520      */
6521     setContent : function(content, loadScripts){
6522         this.bodyEl.update(content, loadScripts);
6523     },
6524
6525     /**
6526      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6527      * @return {Roo.UpdateManager} The UpdateManager
6528      */
6529     getUpdateManager : function(){
6530         return this.bodyEl.getUpdateManager();
6531     },
6532
6533     /**
6534      * Set a URL to be used to load the content for this TabPanelItem.
6535      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6536      * @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)
6537      * @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)
6538      * @return {Roo.UpdateManager} The UpdateManager
6539      */
6540     setUrl : function(url, params, loadOnce){
6541         if(this.refreshDelegate){
6542             this.un('activate', this.refreshDelegate);
6543         }
6544         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6545         this.on("activate", this.refreshDelegate);
6546         return this.bodyEl.getUpdateManager();
6547     },
6548
6549     /** @private */
6550     _handleRefresh : function(url, params, loadOnce){
6551         if(!loadOnce || !this.loaded){
6552             var updater = this.bodyEl.getUpdateManager();
6553             updater.update(url, params, this._setLoaded.createDelegate(this));
6554         }
6555     },
6556
6557     /**
6558      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6559      *   Will fail silently if the setUrl method has not been called.
6560      *   This does not activate the panel, just updates its content.
6561      */
6562     refresh : function(){
6563         if(this.refreshDelegate){
6564            this.loaded = false;
6565            this.refreshDelegate();
6566         }
6567     },
6568
6569     /** @private */
6570     _setLoaded : function(){
6571         this.loaded = true;
6572     },
6573
6574     /** @private */
6575     closeClick : function(e){
6576         var o = {};
6577         e.stopEvent();
6578         this.fireEvent("beforeclose", this, o);
6579         if(o.cancel !== true){
6580             this.tabPanel.removeTab(this.id);
6581         }
6582     },
6583     /**
6584      * The text displayed in the tooltip for the close icon.
6585      * @type String
6586      */
6587     closeText : "Close this tab"
6588 });
6589
6590 /** @private */
6591 Roo.TabPanel.prototype.createStrip = function(container){
6592     var strip = document.createElement("div");
6593     strip.className = "x-tabs-wrap";
6594     container.appendChild(strip);
6595     return strip;
6596 };
6597 /** @private */
6598 Roo.TabPanel.prototype.createStripList = function(strip){
6599     // div wrapper for retard IE
6600     // returns the "tr" element.
6601     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6602         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6603         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6604     return strip.firstChild.firstChild.firstChild.firstChild;
6605 };
6606 /** @private */
6607 Roo.TabPanel.prototype.createBody = function(container){
6608     var body = document.createElement("div");
6609     Roo.id(body, "tab-body");
6610     Roo.fly(body).addClass("x-tabs-body");
6611     container.appendChild(body);
6612     return body;
6613 };
6614 /** @private */
6615 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6616     var body = Roo.getDom(id);
6617     if(!body){
6618         body = document.createElement("div");
6619         body.id = id;
6620     }
6621     Roo.fly(body).addClass("x-tabs-item-body");
6622     bodyEl.insertBefore(body, bodyEl.firstChild);
6623     return body;
6624 };
6625 /** @private */
6626 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6627     var td = document.createElement("td");
6628     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6629     //stripEl.appendChild(td);
6630     if(closable){
6631         td.className = "x-tabs-closable";
6632         if(!this.closeTpl){
6633             this.closeTpl = new Roo.Template(
6634                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6635                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6636                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6637             );
6638         }
6639         var el = this.closeTpl.overwrite(td, {"text": text});
6640         var close = el.getElementsByTagName("div")[0];
6641         var inner = el.getElementsByTagName("em")[0];
6642         return {"el": el, "close": close, "inner": inner};
6643     } else {
6644         if(!this.tabTpl){
6645             this.tabTpl = new Roo.Template(
6646                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6647                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6648             );
6649         }
6650         var el = this.tabTpl.overwrite(td, {"text": text});
6651         var inner = el.getElementsByTagName("em")[0];
6652         return {"el": el, "inner": inner};
6653     }
6654 };/*
6655  * Based on:
6656  * Ext JS Library 1.1.1
6657  * Copyright(c) 2006-2007, Ext JS, LLC.
6658  *
6659  * Originally Released Under LGPL - original licence link has changed is not relivant.
6660  *
6661  * Fork - LGPL
6662  * <script type="text/javascript">
6663  */
6664
6665 /**
6666  * @class Roo.Button
6667  * @extends Roo.util.Observable
6668  * Simple Button class
6669  * @cfg {String} text The button text
6670  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6671  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6672  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6673  * @cfg {Object} scope The scope of the handler
6674  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6675  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6676  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6677  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6678  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6679  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6680    applies if enableToggle = true)
6681  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6682  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6683   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6684  * @constructor
6685  * Create a new button
6686  * @param {Object} config The config object
6687  */
6688 Roo.Button = function(renderTo, config)
6689 {
6690     if (!config) {
6691         config = renderTo;
6692         renderTo = config.renderTo || false;
6693     }
6694     
6695     Roo.apply(this, config);
6696     this.addEvents({
6697         /**
6698              * @event click
6699              * Fires when this button is clicked
6700              * @param {Button} this
6701              * @param {EventObject} e The click event
6702              */
6703             "click" : true,
6704         /**
6705              * @event toggle
6706              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6707              * @param {Button} this
6708              * @param {Boolean} pressed
6709              */
6710             "toggle" : true,
6711         /**
6712              * @event mouseover
6713              * Fires when the mouse hovers over the button
6714              * @param {Button} this
6715              * @param {Event} e The event object
6716              */
6717         'mouseover' : true,
6718         /**
6719              * @event mouseout
6720              * Fires when the mouse exits the button
6721              * @param {Button} this
6722              * @param {Event} e The event object
6723              */
6724         'mouseout': true,
6725          /**
6726              * @event render
6727              * Fires when the button is rendered
6728              * @param {Button} this
6729              */
6730         'render': true
6731     });
6732     if(this.menu){
6733         this.menu = Roo.menu.MenuMgr.get(this.menu);
6734     }
6735     // register listeners first!!  - so render can be captured..
6736     Roo.util.Observable.call(this);
6737     if(renderTo){
6738         this.render(renderTo);
6739     }
6740     
6741   
6742 };
6743
6744 Roo.extend(Roo.Button, Roo.util.Observable, {
6745     /**
6746      * 
6747      */
6748     
6749     /**
6750      * Read-only. True if this button is hidden
6751      * @type Boolean
6752      */
6753     hidden : false,
6754     /**
6755      * Read-only. True if this button is disabled
6756      * @type Boolean
6757      */
6758     disabled : false,
6759     /**
6760      * Read-only. True if this button is pressed (only if enableToggle = true)
6761      * @type Boolean
6762      */
6763     pressed : false,
6764
6765     /**
6766      * @cfg {Number} tabIndex 
6767      * The DOM tabIndex for this button (defaults to undefined)
6768      */
6769     tabIndex : undefined,
6770
6771     /**
6772      * @cfg {Boolean} enableToggle
6773      * True to enable pressed/not pressed toggling (defaults to false)
6774      */
6775     enableToggle: false,
6776     /**
6777      * @cfg {Mixed} menu
6778      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6779      */
6780     menu : undefined,
6781     /**
6782      * @cfg {String} menuAlign
6783      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6784      */
6785     menuAlign : "tl-bl?",
6786
6787     /**
6788      * @cfg {String} iconCls
6789      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6790      */
6791     iconCls : undefined,
6792     /**
6793      * @cfg {String} type
6794      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6795      */
6796     type : 'button',
6797
6798     // private
6799     menuClassTarget: 'tr',
6800
6801     /**
6802      * @cfg {String} clickEvent
6803      * The type of event to map to the button's event handler (defaults to 'click')
6804      */
6805     clickEvent : 'click',
6806
6807     /**
6808      * @cfg {Boolean} handleMouseEvents
6809      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6810      */
6811     handleMouseEvents : true,
6812
6813     /**
6814      * @cfg {String} tooltipType
6815      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6816      */
6817     tooltipType : 'qtip',
6818
6819     /**
6820      * @cfg {String} cls
6821      * A CSS class to apply to the button's main element.
6822      */
6823     
6824     /**
6825      * @cfg {Roo.Template} template (Optional)
6826      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6827      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6828      * require code modifications if required elements (e.g. a button) aren't present.
6829      */
6830
6831     // private
6832     render : function(renderTo){
6833         var btn;
6834         if(this.hideParent){
6835             this.parentEl = Roo.get(renderTo);
6836         }
6837         if(!this.dhconfig){
6838             if(!this.template){
6839                 if(!Roo.Button.buttonTemplate){
6840                     // hideous table template
6841                     Roo.Button.buttonTemplate = new Roo.Template(
6842                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6843                         '<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>',
6844                         "</tr></tbody></table>");
6845                 }
6846                 this.template = Roo.Button.buttonTemplate;
6847             }
6848             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6849             var btnEl = btn.child("button:first");
6850             btnEl.on('focus', this.onFocus, this);
6851             btnEl.on('blur', this.onBlur, this);
6852             if(this.cls){
6853                 btn.addClass(this.cls);
6854             }
6855             if(this.icon){
6856                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6857             }
6858             if(this.iconCls){
6859                 btnEl.addClass(this.iconCls);
6860                 if(!this.cls){
6861                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6862                 }
6863             }
6864             if(this.tabIndex !== undefined){
6865                 btnEl.dom.tabIndex = this.tabIndex;
6866             }
6867             if(this.tooltip){
6868                 if(typeof this.tooltip == 'object'){
6869                     Roo.QuickTips.tips(Roo.apply({
6870                           target: btnEl.id
6871                     }, this.tooltip));
6872                 } else {
6873                     btnEl.dom[this.tooltipType] = this.tooltip;
6874                 }
6875             }
6876         }else{
6877             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6878         }
6879         this.el = btn;
6880         if(this.id){
6881             this.el.dom.id = this.el.id = this.id;
6882         }
6883         if(this.menu){
6884             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6885             this.menu.on("show", this.onMenuShow, this);
6886             this.menu.on("hide", this.onMenuHide, this);
6887         }
6888         btn.addClass("x-btn");
6889         if(Roo.isIE && !Roo.isIE7){
6890             this.autoWidth.defer(1, this);
6891         }else{
6892             this.autoWidth();
6893         }
6894         if(this.handleMouseEvents){
6895             btn.on("mouseover", this.onMouseOver, this);
6896             btn.on("mouseout", this.onMouseOut, this);
6897             btn.on("mousedown", this.onMouseDown, this);
6898         }
6899         btn.on(this.clickEvent, this.onClick, this);
6900         //btn.on("mouseup", this.onMouseUp, this);
6901         if(this.hidden){
6902             this.hide();
6903         }
6904         if(this.disabled){
6905             this.disable();
6906         }
6907         Roo.ButtonToggleMgr.register(this);
6908         if(this.pressed){
6909             this.el.addClass("x-btn-pressed");
6910         }
6911         if(this.repeat){
6912             var repeater = new Roo.util.ClickRepeater(btn,
6913                 typeof this.repeat == "object" ? this.repeat : {}
6914             );
6915             repeater.on("click", this.onClick,  this);
6916         }
6917         
6918         this.fireEvent('render', this);
6919         
6920     },
6921     /**
6922      * Returns the button's underlying element
6923      * @return {Roo.Element} The element
6924      */
6925     getEl : function(){
6926         return this.el;  
6927     },
6928     
6929     /**
6930      * Destroys this Button and removes any listeners.
6931      */
6932     destroy : function(){
6933         Roo.ButtonToggleMgr.unregister(this);
6934         this.el.removeAllListeners();
6935         this.purgeListeners();
6936         this.el.remove();
6937     },
6938
6939     // private
6940     autoWidth : function(){
6941         if(this.el){
6942             this.el.setWidth("auto");
6943             if(Roo.isIE7 && Roo.isStrict){
6944                 var ib = this.el.child('button');
6945                 if(ib && ib.getWidth() > 20){
6946                     ib.clip();
6947                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6948                 }
6949             }
6950             if(this.minWidth){
6951                 if(this.hidden){
6952                     this.el.beginMeasure();
6953                 }
6954                 if(this.el.getWidth() < this.minWidth){
6955                     this.el.setWidth(this.minWidth);
6956                 }
6957                 if(this.hidden){
6958                     this.el.endMeasure();
6959                 }
6960             }
6961         }
6962     },
6963
6964     /**
6965      * Assigns this button's click handler
6966      * @param {Function} handler The function to call when the button is clicked
6967      * @param {Object} scope (optional) Scope for the function passed in
6968      */
6969     setHandler : function(handler, scope){
6970         this.handler = handler;
6971         this.scope = scope;  
6972     },
6973     
6974     /**
6975      * Sets this button's text
6976      * @param {String} text The button text
6977      */
6978     setText : function(text){
6979         this.text = text;
6980         if(this.el){
6981             this.el.child("td.x-btn-center button.x-btn-text").update(text);
6982         }
6983         this.autoWidth();
6984     },
6985     
6986     /**
6987      * Gets the text for this button
6988      * @return {String} The button text
6989      */
6990     getText : function(){
6991         return this.text;  
6992     },
6993     
6994     /**
6995      * Show this button
6996      */
6997     show: function(){
6998         this.hidden = false;
6999         if(this.el){
7000             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
7001         }
7002     },
7003     
7004     /**
7005      * Hide this button
7006      */
7007     hide: function(){
7008         this.hidden = true;
7009         if(this.el){
7010             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
7011         }
7012     },
7013     
7014     /**
7015      * Convenience function for boolean show/hide
7016      * @param {Boolean} visible True to show, false to hide
7017      */
7018     setVisible: function(visible){
7019         if(visible) {
7020             this.show();
7021         }else{
7022             this.hide();
7023         }
7024     },
7025     
7026     /**
7027      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
7028      * @param {Boolean} state (optional) Force a particular state
7029      */
7030     toggle : function(state){
7031         state = state === undefined ? !this.pressed : state;
7032         if(state != this.pressed){
7033             if(state){
7034                 this.el.addClass("x-btn-pressed");
7035                 this.pressed = true;
7036                 this.fireEvent("toggle", this, true);
7037             }else{
7038                 this.el.removeClass("x-btn-pressed");
7039                 this.pressed = false;
7040                 this.fireEvent("toggle", this, false);
7041             }
7042             if(this.toggleHandler){
7043                 this.toggleHandler.call(this.scope || this, this, state);
7044             }
7045         }
7046     },
7047     
7048     /**
7049      * Focus the button
7050      */
7051     focus : function(){
7052         this.el.child('button:first').focus();
7053     },
7054     
7055     /**
7056      * Disable this button
7057      */
7058     disable : function(){
7059         if(this.el){
7060             this.el.addClass("x-btn-disabled");
7061         }
7062         this.disabled = true;
7063     },
7064     
7065     /**
7066      * Enable this button
7067      */
7068     enable : function(){
7069         if(this.el){
7070             this.el.removeClass("x-btn-disabled");
7071         }
7072         this.disabled = false;
7073     },
7074
7075     /**
7076      * Convenience function for boolean enable/disable
7077      * @param {Boolean} enabled True to enable, false to disable
7078      */
7079     setDisabled : function(v){
7080         this[v !== true ? "enable" : "disable"]();
7081     },
7082
7083     // private
7084     onClick : function(e)
7085     {
7086         if(e){
7087             e.preventDefault();
7088         }
7089         if(e.button != 0){
7090             return;
7091         }
7092         if(!this.disabled){
7093             if(this.enableToggle){
7094                 this.toggle();
7095             }
7096             if(this.menu && !this.menu.isVisible()){
7097                 this.menu.show(this.el, this.menuAlign);
7098             }
7099             this.fireEvent("click", this, e);
7100             if(this.handler){
7101                 this.el.removeClass("x-btn-over");
7102                 this.handler.call(this.scope || this, this, e);
7103             }
7104         }
7105     },
7106     // private
7107     onMouseOver : function(e){
7108         if(!this.disabled){
7109             this.el.addClass("x-btn-over");
7110             this.fireEvent('mouseover', this, e);
7111         }
7112     },
7113     // private
7114     onMouseOut : function(e){
7115         if(!e.within(this.el,  true)){
7116             this.el.removeClass("x-btn-over");
7117             this.fireEvent('mouseout', this, e);
7118         }
7119     },
7120     // private
7121     onFocus : function(e){
7122         if(!this.disabled){
7123             this.el.addClass("x-btn-focus");
7124         }
7125     },
7126     // private
7127     onBlur : function(e){
7128         this.el.removeClass("x-btn-focus");
7129     },
7130     // private
7131     onMouseDown : function(e){
7132         if(!this.disabled && e.button == 0){
7133             this.el.addClass("x-btn-click");
7134             Roo.get(document).on('mouseup', this.onMouseUp, this);
7135         }
7136     },
7137     // private
7138     onMouseUp : function(e){
7139         if(e.button == 0){
7140             this.el.removeClass("x-btn-click");
7141             Roo.get(document).un('mouseup', this.onMouseUp, this);
7142         }
7143     },
7144     // private
7145     onMenuShow : function(e){
7146         this.el.addClass("x-btn-menu-active");
7147     },
7148     // private
7149     onMenuHide : function(e){
7150         this.el.removeClass("x-btn-menu-active");
7151     }   
7152 });
7153
7154 // Private utility class used by Button
7155 Roo.ButtonToggleMgr = function(){
7156    var groups = {};
7157    
7158    function toggleGroup(btn, state){
7159        if(state){
7160            var g = groups[btn.toggleGroup];
7161            for(var i = 0, l = g.length; i < l; i++){
7162                if(g[i] != btn){
7163                    g[i].toggle(false);
7164                }
7165            }
7166        }
7167    }
7168    
7169    return {
7170        register : function(btn){
7171            if(!btn.toggleGroup){
7172                return;
7173            }
7174            var g = groups[btn.toggleGroup];
7175            if(!g){
7176                g = groups[btn.toggleGroup] = [];
7177            }
7178            g.push(btn);
7179            btn.on("toggle", toggleGroup);
7180        },
7181        
7182        unregister : function(btn){
7183            if(!btn.toggleGroup){
7184                return;
7185            }
7186            var g = groups[btn.toggleGroup];
7187            if(g){
7188                g.remove(btn);
7189                btn.un("toggle", toggleGroup);
7190            }
7191        }
7192    };
7193 }();/*
7194  * Based on:
7195  * Ext JS Library 1.1.1
7196  * Copyright(c) 2006-2007, Ext JS, LLC.
7197  *
7198  * Originally Released Under LGPL - original licence link has changed is not relivant.
7199  *
7200  * Fork - LGPL
7201  * <script type="text/javascript">
7202  */
7203  
7204 /**
7205  * @class Roo.SplitButton
7206  * @extends Roo.Button
7207  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
7208  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
7209  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
7210  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
7211  * @cfg {String} arrowTooltip The title attribute of the arrow
7212  * @constructor
7213  * Create a new menu button
7214  * @param {String/HTMLElement/Element} renderTo The element to append the button to
7215  * @param {Object} config The config object
7216  */
7217 Roo.SplitButton = function(renderTo, config){
7218     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
7219     /**
7220      * @event arrowclick
7221      * Fires when this button's arrow is clicked
7222      * @param {SplitButton} this
7223      * @param {EventObject} e The click event
7224      */
7225     this.addEvents({"arrowclick":true});
7226 };
7227
7228 Roo.extend(Roo.SplitButton, Roo.Button, {
7229     render : function(renderTo){
7230         // this is one sweet looking template!
7231         var tpl = new Roo.Template(
7232             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
7233             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
7234             '<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>',
7235             "</tbody></table></td><td>",
7236             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
7237             '<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>',
7238             "</tbody></table></td></tr></table>"
7239         );
7240         var btn = tpl.append(renderTo, [this.text, this.type], true);
7241         var btnEl = btn.child("button");
7242         if(this.cls){
7243             btn.addClass(this.cls);
7244         }
7245         if(this.icon){
7246             btnEl.setStyle('background-image', 'url(' +this.icon +')');
7247         }
7248         if(this.iconCls){
7249             btnEl.addClass(this.iconCls);
7250             if(!this.cls){
7251                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
7252             }
7253         }
7254         this.el = btn;
7255         if(this.handleMouseEvents){
7256             btn.on("mouseover", this.onMouseOver, this);
7257             btn.on("mouseout", this.onMouseOut, this);
7258             btn.on("mousedown", this.onMouseDown, this);
7259             btn.on("mouseup", this.onMouseUp, this);
7260         }
7261         btn.on(this.clickEvent, this.onClick, this);
7262         if(this.tooltip){
7263             if(typeof this.tooltip == 'object'){
7264                 Roo.QuickTips.tips(Roo.apply({
7265                       target: btnEl.id
7266                 }, this.tooltip));
7267             } else {
7268                 btnEl.dom[this.tooltipType] = this.tooltip;
7269             }
7270         }
7271         if(this.arrowTooltip){
7272             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
7273         }
7274         if(this.hidden){
7275             this.hide();
7276         }
7277         if(this.disabled){
7278             this.disable();
7279         }
7280         if(this.pressed){
7281             this.el.addClass("x-btn-pressed");
7282         }
7283         if(Roo.isIE && !Roo.isIE7){
7284             this.autoWidth.defer(1, this);
7285         }else{
7286             this.autoWidth();
7287         }
7288         if(this.menu){
7289             this.menu.on("show", this.onMenuShow, this);
7290             this.menu.on("hide", this.onMenuHide, this);
7291         }
7292         this.fireEvent('render', this);
7293     },
7294
7295     // private
7296     autoWidth : function(){
7297         if(this.el){
7298             var tbl = this.el.child("table:first");
7299             var tbl2 = this.el.child("table:last");
7300             this.el.setWidth("auto");
7301             tbl.setWidth("auto");
7302             if(Roo.isIE7 && Roo.isStrict){
7303                 var ib = this.el.child('button:first');
7304                 if(ib && ib.getWidth() > 20){
7305                     ib.clip();
7306                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
7307                 }
7308             }
7309             if(this.minWidth){
7310                 if(this.hidden){
7311                     this.el.beginMeasure();
7312                 }
7313                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
7314                     tbl.setWidth(this.minWidth-tbl2.getWidth());
7315                 }
7316                 if(this.hidden){
7317                     this.el.endMeasure();
7318                 }
7319             }
7320             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
7321         } 
7322     },
7323     /**
7324      * Sets this button's click handler
7325      * @param {Function} handler The function to call when the button is clicked
7326      * @param {Object} scope (optional) Scope for the function passed above
7327      */
7328     setHandler : function(handler, scope){
7329         this.handler = handler;
7330         this.scope = scope;  
7331     },
7332     
7333     /**
7334      * Sets this button's arrow click handler
7335      * @param {Function} handler The function to call when the arrow is clicked
7336      * @param {Object} scope (optional) Scope for the function passed above
7337      */
7338     setArrowHandler : function(handler, scope){
7339         this.arrowHandler = handler;
7340         this.scope = scope;  
7341     },
7342     
7343     /**
7344      * Focus the button
7345      */
7346     focus : function(){
7347         if(this.el){
7348             this.el.child("button:first").focus();
7349         }
7350     },
7351
7352     // private
7353     onClick : function(e){
7354         e.preventDefault();
7355         if(!this.disabled){
7356             if(e.getTarget(".x-btn-menu-arrow-wrap")){
7357                 if(this.menu && !this.menu.isVisible()){
7358                     this.menu.show(this.el, this.menuAlign);
7359                 }
7360                 this.fireEvent("arrowclick", this, e);
7361                 if(this.arrowHandler){
7362                     this.arrowHandler.call(this.scope || this, this, e);
7363                 }
7364             }else{
7365                 this.fireEvent("click", this, e);
7366                 if(this.handler){
7367                     this.handler.call(this.scope || this, this, e);
7368                 }
7369             }
7370         }
7371     },
7372     // private
7373     onMouseDown : function(e){
7374         if(!this.disabled){
7375             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
7376         }
7377     },
7378     // private
7379     onMouseUp : function(e){
7380         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
7381     }   
7382 });
7383
7384
7385 // backwards compat
7386 Roo.MenuButton = Roo.SplitButton;/*
7387  * Based on:
7388  * Ext JS Library 1.1.1
7389  * Copyright(c) 2006-2007, Ext JS, LLC.
7390  *
7391  * Originally Released Under LGPL - original licence link has changed is not relivant.
7392  *
7393  * Fork - LGPL
7394  * <script type="text/javascript">
7395  */
7396
7397 /**
7398  * @class Roo.Toolbar
7399  * Basic Toolbar class.
7400  * @constructor
7401  * Creates a new Toolbar
7402  * @param {Object} container The config object
7403  */ 
7404 Roo.Toolbar = function(container, buttons, config)
7405 {
7406     /// old consturctor format still supported..
7407     if(container instanceof Array){ // omit the container for later rendering
7408         buttons = container;
7409         config = buttons;
7410         container = null;
7411     }
7412     if (typeof(container) == 'object' && container.xtype) {
7413         config = container;
7414         container = config.container;
7415         buttons = config.buttons || []; // not really - use items!!
7416     }
7417     var xitems = [];
7418     if (config && config.items) {
7419         xitems = config.items;
7420         delete config.items;
7421     }
7422     Roo.apply(this, config);
7423     this.buttons = buttons;
7424     
7425     if(container){
7426         this.render(container);
7427     }
7428     this.xitems = xitems;
7429     Roo.each(xitems, function(b) {
7430         this.add(b);
7431     }, this);
7432     
7433 };
7434
7435 Roo.Toolbar.prototype = {
7436     /**
7437      * @cfg {Array} items
7438      * array of button configs or elements to add (will be converted to a MixedCollection)
7439      */
7440     
7441     /**
7442      * @cfg {String/HTMLElement/Element} container
7443      * The id or element that will contain the toolbar
7444      */
7445     // private
7446     render : function(ct){
7447         this.el = Roo.get(ct);
7448         if(this.cls){
7449             this.el.addClass(this.cls);
7450         }
7451         // using a table allows for vertical alignment
7452         // 100% width is needed by Safari...
7453         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7454         this.tr = this.el.child("tr", true);
7455         var autoId = 0;
7456         this.items = new Roo.util.MixedCollection(false, function(o){
7457             return o.id || ("item" + (++autoId));
7458         });
7459         if(this.buttons){
7460             this.add.apply(this, this.buttons);
7461             delete this.buttons;
7462         }
7463     },
7464
7465     /**
7466      * Adds element(s) to the toolbar -- this function takes a variable number of 
7467      * arguments of mixed type and adds them to the toolbar.
7468      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7469      * <ul>
7470      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7471      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7472      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7473      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7474      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7475      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7476      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7477      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7478      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7479      * </ul>
7480      * @param {Mixed} arg2
7481      * @param {Mixed} etc.
7482      */
7483     add : function(){
7484         var a = arguments, l = a.length;
7485         for(var i = 0; i < l; i++){
7486             this._add(a[i]);
7487         }
7488     },
7489     // private..
7490     _add : function(el) {
7491         
7492         if (el.xtype) {
7493             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7494         }
7495         
7496         if (el.applyTo){ // some kind of form field
7497             return this.addField(el);
7498         } 
7499         if (el.render){ // some kind of Toolbar.Item
7500             return this.addItem(el);
7501         }
7502         if (typeof el == "string"){ // string
7503             if(el == "separator" || el == "-"){
7504                 return this.addSeparator();
7505             }
7506             if (el == " "){
7507                 return this.addSpacer();
7508             }
7509             if(el == "->"){
7510                 return this.addFill();
7511             }
7512             return this.addText(el);
7513             
7514         }
7515         if(el.tagName){ // element
7516             return this.addElement(el);
7517         }
7518         if(typeof el == "object"){ // must be button config?
7519             return this.addButton(el);
7520         }
7521         // and now what?!?!
7522         return false;
7523         
7524     },
7525     
7526     /**
7527      * Add an Xtype element
7528      * @param {Object} xtype Xtype Object
7529      * @return {Object} created Object
7530      */
7531     addxtype : function(e){
7532         return this.add(e);  
7533     },
7534     
7535     /**
7536      * Returns the Element for this toolbar.
7537      * @return {Roo.Element}
7538      */
7539     getEl : function(){
7540         return this.el;  
7541     },
7542     
7543     /**
7544      * Adds a separator
7545      * @return {Roo.Toolbar.Item} The separator item
7546      */
7547     addSeparator : function(){
7548         return this.addItem(new Roo.Toolbar.Separator());
7549     },
7550
7551     /**
7552      * Adds a spacer element
7553      * @return {Roo.Toolbar.Spacer} The spacer item
7554      */
7555     addSpacer : function(){
7556         return this.addItem(new Roo.Toolbar.Spacer());
7557     },
7558
7559     /**
7560      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7561      * @return {Roo.Toolbar.Fill} The fill item
7562      */
7563     addFill : function(){
7564         return this.addItem(new Roo.Toolbar.Fill());
7565     },
7566
7567     /**
7568      * Adds any standard HTML element to the toolbar
7569      * @param {String/HTMLElement/Element} el The element or id of the element to add
7570      * @return {Roo.Toolbar.Item} The element's item
7571      */
7572     addElement : function(el){
7573         return this.addItem(new Roo.Toolbar.Item(el));
7574     },
7575     /**
7576      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7577      * @type Roo.util.MixedCollection  
7578      */
7579     items : false,
7580      
7581     /**
7582      * Adds any Toolbar.Item or subclass
7583      * @param {Roo.Toolbar.Item} item
7584      * @return {Roo.Toolbar.Item} The item
7585      */
7586     addItem : function(item){
7587         var td = this.nextBlock();
7588         item.render(td);
7589         this.items.add(item);
7590         return item;
7591     },
7592     
7593     /**
7594      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7595      * @param {Object/Array} config A button config or array of configs
7596      * @return {Roo.Toolbar.Button/Array}
7597      */
7598     addButton : function(config){
7599         if(config instanceof Array){
7600             var buttons = [];
7601             for(var i = 0, len = config.length; i < len; i++) {
7602                 buttons.push(this.addButton(config[i]));
7603             }
7604             return buttons;
7605         }
7606         var b = config;
7607         if(!(config instanceof Roo.Toolbar.Button)){
7608             b = config.split ?
7609                 new Roo.Toolbar.SplitButton(config) :
7610                 new Roo.Toolbar.Button(config);
7611         }
7612         var td = this.nextBlock();
7613         b.render(td);
7614         this.items.add(b);
7615         return b;
7616     },
7617     
7618     /**
7619      * Adds text to the toolbar
7620      * @param {String} text The text to add
7621      * @return {Roo.Toolbar.Item} The element's item
7622      */
7623     addText : function(text){
7624         return this.addItem(new Roo.Toolbar.TextItem(text));
7625     },
7626     
7627     /**
7628      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7629      * @param {Number} index The index where the item is to be inserted
7630      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7631      * @return {Roo.Toolbar.Button/Item}
7632      */
7633     insertButton : function(index, item){
7634         if(item instanceof Array){
7635             var buttons = [];
7636             for(var i = 0, len = item.length; i < len; i++) {
7637                buttons.push(this.insertButton(index + i, item[i]));
7638             }
7639             return buttons;
7640         }
7641         if (!(item instanceof Roo.Toolbar.Button)){
7642            item = new Roo.Toolbar.Button(item);
7643         }
7644         var td = document.createElement("td");
7645         this.tr.insertBefore(td, this.tr.childNodes[index]);
7646         item.render(td);
7647         this.items.insert(index, item);
7648         return item;
7649     },
7650     
7651     /**
7652      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7653      * @param {Object} config
7654      * @return {Roo.Toolbar.Item} The element's item
7655      */
7656     addDom : function(config, returnEl){
7657         var td = this.nextBlock();
7658         Roo.DomHelper.overwrite(td, config);
7659         var ti = new Roo.Toolbar.Item(td.firstChild);
7660         ti.render(td);
7661         this.items.add(ti);
7662         return ti;
7663     },
7664
7665     /**
7666      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7667      * @type Roo.util.MixedCollection  
7668      */
7669     fields : false,
7670     
7671     /**
7672      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7673      * Note: the field should not have been rendered yet. For a field that has already been
7674      * rendered, use {@link #addElement}.
7675      * @param {Roo.form.Field} field
7676      * @return {Roo.ToolbarItem}
7677      */
7678      
7679       
7680     addField : function(field) {
7681         if (!this.fields) {
7682             var autoId = 0;
7683             this.fields = new Roo.util.MixedCollection(false, function(o){
7684                 return o.id || ("item" + (++autoId));
7685             });
7686
7687         }
7688         
7689         var td = this.nextBlock();
7690         field.render(td);
7691         var ti = new Roo.Toolbar.Item(td.firstChild);
7692         ti.render(td);
7693         this.items.add(ti);
7694         this.fields.add(field);
7695         return ti;
7696     },
7697     /**
7698      * Hide the toolbar
7699      * @method hide
7700      */
7701      
7702       
7703     hide : function()
7704     {
7705         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7706         this.el.child('div').hide();
7707     },
7708     /**
7709      * Show the toolbar
7710      * @method show
7711      */
7712     show : function()
7713     {
7714         this.el.child('div').show();
7715     },
7716       
7717     // private
7718     nextBlock : function(){
7719         var td = document.createElement("td");
7720         this.tr.appendChild(td);
7721         return td;
7722     },
7723
7724     // private
7725     destroy : function(){
7726         if(this.items){ // rendered?
7727             Roo.destroy.apply(Roo, this.items.items);
7728         }
7729         if(this.fields){ // rendered?
7730             Roo.destroy.apply(Roo, this.fields.items);
7731         }
7732         Roo.Element.uncache(this.el, this.tr);
7733     }
7734 };
7735
7736 /**
7737  * @class Roo.Toolbar.Item
7738  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7739  * @constructor
7740  * Creates a new Item
7741  * @param {HTMLElement} el 
7742  */
7743 Roo.Toolbar.Item = function(el){
7744     var cfg = {};
7745     if (typeof (el.xtype) != 'undefined') {
7746         cfg = el;
7747         el = cfg.el;
7748     }
7749     
7750     this.el = Roo.getDom(el);
7751     this.id = Roo.id(this.el);
7752     this.hidden = false;
7753     
7754     this.addEvents({
7755          /**
7756              * @event render
7757              * Fires when the button is rendered
7758              * @param {Button} this
7759              */
7760         'render': true
7761     });
7762     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7763 };
7764 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7765 //Roo.Toolbar.Item.prototype = {
7766     
7767     /**
7768      * Get this item's HTML Element
7769      * @return {HTMLElement}
7770      */
7771     getEl : function(){
7772        return this.el;  
7773     },
7774
7775     // private
7776     render : function(td){
7777         
7778          this.td = td;
7779         td.appendChild(this.el);
7780         
7781         this.fireEvent('render', this);
7782     },
7783     
7784     /**
7785      * Removes and destroys this item.
7786      */
7787     destroy : function(){
7788         this.td.parentNode.removeChild(this.td);
7789     },
7790     
7791     /**
7792      * Shows this item.
7793      */
7794     show: function(){
7795         this.hidden = false;
7796         this.td.style.display = "";
7797     },
7798     
7799     /**
7800      * Hides this item.
7801      */
7802     hide: function(){
7803         this.hidden = true;
7804         this.td.style.display = "none";
7805     },
7806     
7807     /**
7808      * Convenience function for boolean show/hide.
7809      * @param {Boolean} visible true to show/false to hide
7810      */
7811     setVisible: function(visible){
7812         if(visible) {
7813             this.show();
7814         }else{
7815             this.hide();
7816         }
7817     },
7818     
7819     /**
7820      * Try to focus this item.
7821      */
7822     focus : function(){
7823         Roo.fly(this.el).focus();
7824     },
7825     
7826     /**
7827      * Disables this item.
7828      */
7829     disable : function(){
7830         Roo.fly(this.td).addClass("x-item-disabled");
7831         this.disabled = true;
7832         this.el.disabled = true;
7833     },
7834     
7835     /**
7836      * Enables this item.
7837      */
7838     enable : function(){
7839         Roo.fly(this.td).removeClass("x-item-disabled");
7840         this.disabled = false;
7841         this.el.disabled = false;
7842     }
7843 });
7844
7845
7846 /**
7847  * @class Roo.Toolbar.Separator
7848  * @extends Roo.Toolbar.Item
7849  * A simple toolbar separator class
7850  * @constructor
7851  * Creates a new Separator
7852  */
7853 Roo.Toolbar.Separator = function(cfg){
7854     
7855     var s = document.createElement("span");
7856     s.className = "ytb-sep";
7857     if (cfg) {
7858         cfg.el = s;
7859     }
7860     
7861     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7862 };
7863 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7864     enable:Roo.emptyFn,
7865     disable:Roo.emptyFn,
7866     focus:Roo.emptyFn
7867 });
7868
7869 /**
7870  * @class Roo.Toolbar.Spacer
7871  * @extends Roo.Toolbar.Item
7872  * A simple element that adds extra horizontal space to a toolbar.
7873  * @constructor
7874  * Creates a new Spacer
7875  */
7876 Roo.Toolbar.Spacer = function(cfg){
7877     var s = document.createElement("div");
7878     s.className = "ytb-spacer";
7879     if (cfg) {
7880         cfg.el = s;
7881     }
7882     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7883 };
7884 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7885     enable:Roo.emptyFn,
7886     disable:Roo.emptyFn,
7887     focus:Roo.emptyFn
7888 });
7889
7890 /**
7891  * @class Roo.Toolbar.Fill
7892  * @extends Roo.Toolbar.Spacer
7893  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7894  * @constructor
7895  * Creates a new Spacer
7896  */
7897 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7898     // private
7899     render : function(td){
7900         td.style.width = '100%';
7901         Roo.Toolbar.Fill.superclass.render.call(this, td);
7902     }
7903 });
7904
7905 /**
7906  * @class Roo.Toolbar.TextItem
7907  * @extends Roo.Toolbar.Item
7908  * A simple class that renders text directly into a toolbar.
7909  * @constructor
7910  * Creates a new TextItem
7911  * @param {String} text
7912  */
7913 Roo.Toolbar.TextItem = function(cfg){
7914     var  text = cfg || "";
7915     if (typeof(cfg) == 'object') {
7916         text = cfg.text || "";
7917     }  else {
7918         cfg = null;
7919     }
7920     var s = document.createElement("span");
7921     s.className = "ytb-text";
7922     s.innerHTML = text;
7923     if (cfg) {
7924         cfg.el  = s;
7925     }
7926     
7927     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7928 };
7929 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7930     
7931      
7932     enable:Roo.emptyFn,
7933     disable:Roo.emptyFn,
7934     focus:Roo.emptyFn
7935 });
7936
7937 /**
7938  * @class Roo.Toolbar.Button
7939  * @extends Roo.Button
7940  * A button that renders into a toolbar.
7941  * @constructor
7942  * Creates a new Button
7943  * @param {Object} config A standard {@link Roo.Button} config object
7944  */
7945 Roo.Toolbar.Button = function(config){
7946     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7947 };
7948 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
7949     render : function(td){
7950         this.td = td;
7951         Roo.Toolbar.Button.superclass.render.call(this, td);
7952     },
7953     
7954     /**
7955      * Removes and destroys this button
7956      */
7957     destroy : function(){
7958         Roo.Toolbar.Button.superclass.destroy.call(this);
7959         this.td.parentNode.removeChild(this.td);
7960     },
7961     
7962     /**
7963      * Shows this button
7964      */
7965     show: function(){
7966         this.hidden = false;
7967         this.td.style.display = "";
7968     },
7969     
7970     /**
7971      * Hides this button
7972      */
7973     hide: function(){
7974         this.hidden = true;
7975         this.td.style.display = "none";
7976     },
7977
7978     /**
7979      * Disables this item
7980      */
7981     disable : function(){
7982         Roo.fly(this.td).addClass("x-item-disabled");
7983         this.disabled = true;
7984     },
7985
7986     /**
7987      * Enables this item
7988      */
7989     enable : function(){
7990         Roo.fly(this.td).removeClass("x-item-disabled");
7991         this.disabled = false;
7992     }
7993 });
7994 // backwards compat
7995 Roo.ToolbarButton = Roo.Toolbar.Button;
7996
7997 /**
7998  * @class Roo.Toolbar.SplitButton
7999  * @extends Roo.SplitButton
8000  * A menu button that renders into a toolbar.
8001  * @constructor
8002  * Creates a new SplitButton
8003  * @param {Object} config A standard {@link Roo.SplitButton} config object
8004  */
8005 Roo.Toolbar.SplitButton = function(config){
8006     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
8007 };
8008 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
8009     render : function(td){
8010         this.td = td;
8011         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
8012     },
8013     
8014     /**
8015      * Removes and destroys this button
8016      */
8017     destroy : function(){
8018         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
8019         this.td.parentNode.removeChild(this.td);
8020     },
8021     
8022     /**
8023      * Shows this button
8024      */
8025     show: function(){
8026         this.hidden = false;
8027         this.td.style.display = "";
8028     },
8029     
8030     /**
8031      * Hides this button
8032      */
8033     hide: function(){
8034         this.hidden = true;
8035         this.td.style.display = "none";
8036     }
8037 });
8038
8039 // backwards compat
8040 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
8041  * Based on:
8042  * Ext JS Library 1.1.1
8043  * Copyright(c) 2006-2007, Ext JS, LLC.
8044  *
8045  * Originally Released Under LGPL - original licence link has changed is not relivant.
8046  *
8047  * Fork - LGPL
8048  * <script type="text/javascript">
8049  */
8050  
8051 /**
8052  * @class Roo.PagingToolbar
8053  * @extends Roo.Toolbar
8054  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
8055  * @constructor
8056  * Create a new PagingToolbar
8057  * @param {Object} config The config object
8058  */
8059 Roo.PagingToolbar = function(el, ds, config)
8060 {
8061     // old args format still supported... - xtype is prefered..
8062     if (typeof(el) == 'object' && el.xtype) {
8063         // created from xtype...
8064         config = el;
8065         ds = el.dataSource;
8066         el = config.container;
8067     }
8068     var items = [];
8069     if (config.items) {
8070         items = config.items;
8071         config.items = [];
8072     }
8073     
8074     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
8075     this.ds = ds;
8076     this.cursor = 0;
8077     this.renderButtons(this.el);
8078     this.bind(ds);
8079     
8080     // supprot items array.
8081    
8082     Roo.each(items, function(e) {
8083         this.add(Roo.factory(e));
8084     },this);
8085     
8086 };
8087
8088 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
8089     /**
8090      * @cfg {Roo.data.Store} dataSource
8091      * The underlying data store providing the paged data
8092      */
8093     /**
8094      * @cfg {String/HTMLElement/Element} container
8095      * container The id or element that will contain the toolbar
8096      */
8097     /**
8098      * @cfg {Boolean} displayInfo
8099      * True to display the displayMsg (defaults to false)
8100      */
8101     /**
8102      * @cfg {Number} pageSize
8103      * The number of records to display per page (defaults to 20)
8104      */
8105     pageSize: 20,
8106     /**
8107      * @cfg {String} displayMsg
8108      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
8109      */
8110     displayMsg : 'Displaying {0} - {1} of {2}',
8111     /**
8112      * @cfg {String} emptyMsg
8113      * The message to display when no records are found (defaults to "No data to display")
8114      */
8115     emptyMsg : 'No data to display',
8116     /**
8117      * Customizable piece of the default paging text (defaults to "Page")
8118      * @type String
8119      */
8120     beforePageText : "Page",
8121     /**
8122      * Customizable piece of the default paging text (defaults to "of %0")
8123      * @type String
8124      */
8125     afterPageText : "of {0}",
8126     /**
8127      * Customizable piece of the default paging text (defaults to "First Page")
8128      * @type String
8129      */
8130     firstText : "First Page",
8131     /**
8132      * Customizable piece of the default paging text (defaults to "Previous Page")
8133      * @type String
8134      */
8135     prevText : "Previous Page",
8136     /**
8137      * Customizable piece of the default paging text (defaults to "Next Page")
8138      * @type String
8139      */
8140     nextText : "Next Page",
8141     /**
8142      * Customizable piece of the default paging text (defaults to "Last Page")
8143      * @type String
8144      */
8145     lastText : "Last Page",
8146     /**
8147      * Customizable piece of the default paging text (defaults to "Refresh")
8148      * @type String
8149      */
8150     refreshText : "Refresh",
8151
8152     // private
8153     renderButtons : function(el){
8154         Roo.PagingToolbar.superclass.render.call(this, el);
8155         this.first = this.addButton({
8156             tooltip: this.firstText,
8157             cls: "x-btn-icon x-grid-page-first",
8158             disabled: true,
8159             handler: this.onClick.createDelegate(this, ["first"])
8160         });
8161         this.prev = this.addButton({
8162             tooltip: this.prevText,
8163             cls: "x-btn-icon x-grid-page-prev",
8164             disabled: true,
8165             handler: this.onClick.createDelegate(this, ["prev"])
8166         });
8167         //this.addSeparator();
8168         this.add(this.beforePageText);
8169         this.field = Roo.get(this.addDom({
8170            tag: "input",
8171            type: "text",
8172            size: "3",
8173            value: "1",
8174            cls: "x-grid-page-number"
8175         }).el);
8176         this.field.on("keydown", this.onPagingKeydown, this);
8177         this.field.on("focus", function(){this.dom.select();});
8178         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
8179         this.field.setHeight(18);
8180         //this.addSeparator();
8181         this.next = this.addButton({
8182             tooltip: this.nextText,
8183             cls: "x-btn-icon x-grid-page-next",
8184             disabled: true,
8185             handler: this.onClick.createDelegate(this, ["next"])
8186         });
8187         this.last = this.addButton({
8188             tooltip: this.lastText,
8189             cls: "x-btn-icon x-grid-page-last",
8190             disabled: true,
8191             handler: this.onClick.createDelegate(this, ["last"])
8192         });
8193         //this.addSeparator();
8194         this.loading = this.addButton({
8195             tooltip: this.refreshText,
8196             cls: "x-btn-icon x-grid-loading",
8197             handler: this.onClick.createDelegate(this, ["refresh"])
8198         });
8199
8200         if(this.displayInfo){
8201             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
8202         }
8203     },
8204
8205     // private
8206     updateInfo : function(){
8207         if(this.displayEl){
8208             var count = this.ds.getCount();
8209             var msg = count == 0 ?
8210                 this.emptyMsg :
8211                 String.format(
8212                     this.displayMsg,
8213                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
8214                 );
8215             this.displayEl.update(msg);
8216         }
8217     },
8218
8219     // private
8220     onLoad : function(ds, r, o){
8221        this.cursor = o.params ? o.params.start : 0;
8222        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
8223
8224        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
8225        this.field.dom.value = ap;
8226        this.first.setDisabled(ap == 1);
8227        this.prev.setDisabled(ap == 1);
8228        this.next.setDisabled(ap == ps);
8229        this.last.setDisabled(ap == ps);
8230        this.loading.enable();
8231        this.updateInfo();
8232     },
8233
8234     // private
8235     getPageData : function(){
8236         var total = this.ds.getTotalCount();
8237         return {
8238             total : total,
8239             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
8240             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
8241         };
8242     },
8243
8244     // private
8245     onLoadError : function(){
8246         this.loading.enable();
8247     },
8248
8249     // private
8250     onPagingKeydown : function(e){
8251         var k = e.getKey();
8252         var d = this.getPageData();
8253         if(k == e.RETURN){
8254             var v = this.field.dom.value, pageNum;
8255             if(!v || isNaN(pageNum = parseInt(v, 10))){
8256                 this.field.dom.value = d.activePage;
8257                 return;
8258             }
8259             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
8260             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8261             e.stopEvent();
8262         }
8263         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))
8264         {
8265           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
8266           this.field.dom.value = pageNum;
8267           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
8268           e.stopEvent();
8269         }
8270         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
8271         {
8272           var v = this.field.dom.value, pageNum; 
8273           var increment = (e.shiftKey) ? 10 : 1;
8274           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
8275             increment *= -1;
8276           }
8277           if(!v || isNaN(pageNum = parseInt(v, 10))) {
8278             this.field.dom.value = d.activePage;
8279             return;
8280           }
8281           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
8282           {
8283             this.field.dom.value = parseInt(v, 10) + increment;
8284             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
8285             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8286           }
8287           e.stopEvent();
8288         }
8289     },
8290
8291     // private
8292     beforeLoad : function(){
8293         if(this.loading){
8294             this.loading.disable();
8295         }
8296     },
8297
8298     // private
8299     onClick : function(which){
8300         var ds = this.ds;
8301         switch(which){
8302             case "first":
8303                 ds.load({params:{start: 0, limit: this.pageSize}});
8304             break;
8305             case "prev":
8306                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
8307             break;
8308             case "next":
8309                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
8310             break;
8311             case "last":
8312                 var total = ds.getTotalCount();
8313                 var extra = total % this.pageSize;
8314                 var lastStart = extra ? (total - extra) : total-this.pageSize;
8315                 ds.load({params:{start: lastStart, limit: this.pageSize}});
8316             break;
8317             case "refresh":
8318                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
8319             break;
8320         }
8321     },
8322
8323     /**
8324      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
8325      * @param {Roo.data.Store} store The data store to unbind
8326      */
8327     unbind : function(ds){
8328         ds.un("beforeload", this.beforeLoad, this);
8329         ds.un("load", this.onLoad, this);
8330         ds.un("loadexception", this.onLoadError, this);
8331         ds.un("remove", this.updateInfo, this);
8332         ds.un("add", this.updateInfo, this);
8333         this.ds = undefined;
8334     },
8335
8336     /**
8337      * Binds the paging toolbar to the specified {@link Roo.data.Store}
8338      * @param {Roo.data.Store} store The data store to bind
8339      */
8340     bind : function(ds){
8341         ds.on("beforeload", this.beforeLoad, this);
8342         ds.on("load", this.onLoad, this);
8343         ds.on("loadexception", this.onLoadError, this);
8344         ds.on("remove", this.updateInfo, this);
8345         ds.on("add", this.updateInfo, this);
8346         this.ds = ds;
8347     }
8348 });/*
8349  * Based on:
8350  * Ext JS Library 1.1.1
8351  * Copyright(c) 2006-2007, Ext JS, LLC.
8352  *
8353  * Originally Released Under LGPL - original licence link has changed is not relivant.
8354  *
8355  * Fork - LGPL
8356  * <script type="text/javascript">
8357  */
8358
8359 /**
8360  * @class Roo.Resizable
8361  * @extends Roo.util.Observable
8362  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
8363  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
8364  * 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
8365  * the element will be wrapped for you automatically.</p>
8366  * <p>Here is the list of valid resize handles:</p>
8367  * <pre>
8368 Value   Description
8369 ------  -------------------
8370  'n'     north
8371  's'     south
8372  'e'     east
8373  'w'     west
8374  'nw'    northwest
8375  'sw'    southwest
8376  'se'    southeast
8377  'ne'    northeast
8378  'hd'    horizontal drag
8379  'all'   all
8380 </pre>
8381  * <p>Here's an example showing the creation of a typical Resizable:</p>
8382  * <pre><code>
8383 var resizer = new Roo.Resizable("element-id", {
8384     handles: 'all',
8385     minWidth: 200,
8386     minHeight: 100,
8387     maxWidth: 500,
8388     maxHeight: 400,
8389     pinned: true
8390 });
8391 resizer.on("resize", myHandler);
8392 </code></pre>
8393  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8394  * resizer.east.setDisplayed(false);</p>
8395  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8396  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8397  * resize operation's new size (defaults to [0, 0])
8398  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8399  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8400  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8401  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8402  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8403  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8404  * @cfg {Number} width The width of the element in pixels (defaults to null)
8405  * @cfg {Number} height The height of the element in pixels (defaults to null)
8406  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8407  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8408  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8409  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8410  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8411  * in favor of the handles config option (defaults to false)
8412  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8413  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8414  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8415  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8416  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8417  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8418  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8419  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8420  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8421  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8422  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8423  * @constructor
8424  * Create a new resizable component
8425  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8426  * @param {Object} config configuration options
8427   */
8428 Roo.Resizable = function(el, config)
8429 {
8430     this.el = Roo.get(el);
8431
8432     if(config && config.wrap){
8433         config.resizeChild = this.el;
8434         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8435         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8436         this.el.setStyle("overflow", "hidden");
8437         this.el.setPositioning(config.resizeChild.getPositioning());
8438         config.resizeChild.clearPositioning();
8439         if(!config.width || !config.height){
8440             var csize = config.resizeChild.getSize();
8441             this.el.setSize(csize.width, csize.height);
8442         }
8443         if(config.pinned && !config.adjustments){
8444             config.adjustments = "auto";
8445         }
8446     }
8447
8448     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8449     this.proxy.unselectable();
8450     this.proxy.enableDisplayMode('block');
8451
8452     Roo.apply(this, config);
8453
8454     if(this.pinned){
8455         this.disableTrackOver = true;
8456         this.el.addClass("x-resizable-pinned");
8457     }
8458     // if the element isn't positioned, make it relative
8459     var position = this.el.getStyle("position");
8460     if(position != "absolute" && position != "fixed"){
8461         this.el.setStyle("position", "relative");
8462     }
8463     if(!this.handles){ // no handles passed, must be legacy style
8464         this.handles = 's,e,se';
8465         if(this.multiDirectional){
8466             this.handles += ',n,w';
8467         }
8468     }
8469     if(this.handles == "all"){
8470         this.handles = "n s e w ne nw se sw";
8471     }
8472     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8473     var ps = Roo.Resizable.positions;
8474     for(var i = 0, len = hs.length; i < len; i++){
8475         if(hs[i] && ps[hs[i]]){
8476             var pos = ps[hs[i]];
8477             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8478         }
8479     }
8480     // legacy
8481     this.corner = this.southeast;
8482     
8483     // updateBox = the box can move..
8484     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8485         this.updateBox = true;
8486     }
8487
8488     this.activeHandle = null;
8489
8490     if(this.resizeChild){
8491         if(typeof this.resizeChild == "boolean"){
8492             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8493         }else{
8494             this.resizeChild = Roo.get(this.resizeChild, true);
8495         }
8496     }
8497     
8498     if(this.adjustments == "auto"){
8499         var rc = this.resizeChild;
8500         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8501         if(rc && (hw || hn)){
8502             rc.position("relative");
8503             rc.setLeft(hw ? hw.el.getWidth() : 0);
8504             rc.setTop(hn ? hn.el.getHeight() : 0);
8505         }
8506         this.adjustments = [
8507             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8508             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8509         ];
8510     }
8511
8512     if(this.draggable){
8513         this.dd = this.dynamic ?
8514             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8515         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8516     }
8517
8518     // public events
8519     this.addEvents({
8520         /**
8521          * @event beforeresize
8522          * Fired before resize is allowed. Set enabled to false to cancel resize.
8523          * @param {Roo.Resizable} this
8524          * @param {Roo.EventObject} e The mousedown event
8525          */
8526         "beforeresize" : true,
8527         /**
8528          * @event resizing
8529          * Fired a resizing.
8530          * @param {Roo.Resizable} this
8531          * @param {Number} x The new x position
8532          * @param {Number} y The new y position
8533          * @param {Number} w The new w width
8534          * @param {Number} h The new h hight
8535          * @param {Roo.EventObject} e The mouseup event
8536          */
8537         "resizing" : true,
8538         /**
8539          * @event resize
8540          * Fired after a resize.
8541          * @param {Roo.Resizable} this
8542          * @param {Number} width The new width
8543          * @param {Number} height The new height
8544          * @param {Roo.EventObject} e The mouseup event
8545          */
8546         "resize" : true
8547     });
8548
8549     if(this.width !== null && this.height !== null){
8550         this.resizeTo(this.width, this.height);
8551     }else{
8552         this.updateChildSize();
8553     }
8554     if(Roo.isIE){
8555         this.el.dom.style.zoom = 1;
8556     }
8557     Roo.Resizable.superclass.constructor.call(this);
8558 };
8559
8560 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8561         resizeChild : false,
8562         adjustments : [0, 0],
8563         minWidth : 5,
8564         minHeight : 5,
8565         maxWidth : 10000,
8566         maxHeight : 10000,
8567         enabled : true,
8568         animate : false,
8569         duration : .35,
8570         dynamic : false,
8571         handles : false,
8572         multiDirectional : false,
8573         disableTrackOver : false,
8574         easing : 'easeOutStrong',
8575         widthIncrement : 0,
8576         heightIncrement : 0,
8577         pinned : false,
8578         width : null,
8579         height : null,
8580         preserveRatio : false,
8581         transparent: false,
8582         minX: 0,
8583         minY: 0,
8584         draggable: false,
8585
8586         /**
8587          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8588          */
8589         constrainTo: undefined,
8590         /**
8591          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8592          */
8593         resizeRegion: undefined,
8594
8595
8596     /**
8597      * Perform a manual resize
8598      * @param {Number} width
8599      * @param {Number} height
8600      */
8601     resizeTo : function(width, height){
8602         this.el.setSize(width, height);
8603         this.updateChildSize();
8604         this.fireEvent("resize", this, width, height, null);
8605     },
8606
8607     // private
8608     startSizing : function(e, handle){
8609         this.fireEvent("beforeresize", this, e);
8610         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8611
8612             if(!this.overlay){
8613                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8614                 this.overlay.unselectable();
8615                 this.overlay.enableDisplayMode("block");
8616                 this.overlay.on("mousemove", this.onMouseMove, this);
8617                 this.overlay.on("mouseup", this.onMouseUp, this);
8618             }
8619             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8620
8621             this.resizing = true;
8622             this.startBox = this.el.getBox();
8623             this.startPoint = e.getXY();
8624             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8625                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8626
8627             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8628             this.overlay.show();
8629
8630             if(this.constrainTo) {
8631                 var ct = Roo.get(this.constrainTo);
8632                 this.resizeRegion = ct.getRegion().adjust(
8633                     ct.getFrameWidth('t'),
8634                     ct.getFrameWidth('l'),
8635                     -ct.getFrameWidth('b'),
8636                     -ct.getFrameWidth('r')
8637                 );
8638             }
8639
8640             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8641             this.proxy.show();
8642             this.proxy.setBox(this.startBox);
8643             if(!this.dynamic){
8644                 this.proxy.setStyle('visibility', 'visible');
8645             }
8646         }
8647     },
8648
8649     // private
8650     onMouseDown : function(handle, e){
8651         if(this.enabled){
8652             e.stopEvent();
8653             this.activeHandle = handle;
8654             this.startSizing(e, handle);
8655         }
8656     },
8657
8658     // private
8659     onMouseUp : function(e){
8660         var size = this.resizeElement();
8661         this.resizing = false;
8662         this.handleOut();
8663         this.overlay.hide();
8664         this.proxy.hide();
8665         this.fireEvent("resize", this, size.width, size.height, e);
8666     },
8667
8668     // private
8669     updateChildSize : function(){
8670         
8671         if(this.resizeChild){
8672             var el = this.el;
8673             var child = this.resizeChild;
8674             var adj = this.adjustments;
8675             if(el.dom.offsetWidth){
8676                 var b = el.getSize(true);
8677                 child.setSize(b.width+adj[0], b.height+adj[1]);
8678             }
8679             // Second call here for IE
8680             // The first call enables instant resizing and
8681             // the second call corrects scroll bars if they
8682             // exist
8683             if(Roo.isIE){
8684                 setTimeout(function(){
8685                     if(el.dom.offsetWidth){
8686                         var b = el.getSize(true);
8687                         child.setSize(b.width+adj[0], b.height+adj[1]);
8688                     }
8689                 }, 10);
8690             }
8691         }
8692     },
8693
8694     // private
8695     snap : function(value, inc, min){
8696         if(!inc || !value) {
8697             return value;
8698         }
8699         var newValue = value;
8700         var m = value % inc;
8701         if(m > 0){
8702             if(m > (inc/2)){
8703                 newValue = value + (inc-m);
8704             }else{
8705                 newValue = value - m;
8706             }
8707         }
8708         return Math.max(min, newValue);
8709     },
8710
8711     // private
8712     resizeElement : function(){
8713         var box = this.proxy.getBox();
8714         if(this.updateBox){
8715             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8716         }else{
8717             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8718         }
8719         this.updateChildSize();
8720         if(!this.dynamic){
8721             this.proxy.hide();
8722         }
8723         return box;
8724     },
8725
8726     // private
8727     constrain : function(v, diff, m, mx){
8728         if(v - diff < m){
8729             diff = v - m;
8730         }else if(v - diff > mx){
8731             diff = mx - v;
8732         }
8733         return diff;
8734     },
8735
8736     // private
8737     onMouseMove : function(e){
8738         
8739         if(this.enabled){
8740             try{// try catch so if something goes wrong the user doesn't get hung
8741
8742             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8743                 return;
8744             }
8745
8746             //var curXY = this.startPoint;
8747             var curSize = this.curSize || this.startBox;
8748             var x = this.startBox.x, y = this.startBox.y;
8749             var ox = x, oy = y;
8750             var w = curSize.width, h = curSize.height;
8751             var ow = w, oh = h;
8752             var mw = this.minWidth, mh = this.minHeight;
8753             var mxw = this.maxWidth, mxh = this.maxHeight;
8754             var wi = this.widthIncrement;
8755             var hi = this.heightIncrement;
8756
8757             var eventXY = e.getXY();
8758             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8759             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8760
8761             var pos = this.activeHandle.position;
8762
8763             switch(pos){
8764                 case "east":
8765                     w += diffX;
8766                     w = Math.min(Math.max(mw, w), mxw);
8767                     break;
8768              
8769                 case "south":
8770                     h += diffY;
8771                     h = Math.min(Math.max(mh, h), mxh);
8772                     break;
8773                 case "southeast":
8774                     w += diffX;
8775                     h += diffY;
8776                     w = Math.min(Math.max(mw, w), mxw);
8777                     h = Math.min(Math.max(mh, h), mxh);
8778                     break;
8779                 case "north":
8780                     diffY = this.constrain(h, diffY, mh, mxh);
8781                     y += diffY;
8782                     h -= diffY;
8783                     break;
8784                 case "hdrag":
8785                     
8786                     if (wi) {
8787                         var adiffX = Math.abs(diffX);
8788                         var sub = (adiffX % wi); // how much 
8789                         if (sub > (wi/2)) { // far enough to snap
8790                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8791                         } else {
8792                             // remove difference.. 
8793                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8794                         }
8795                     }
8796                     x += diffX;
8797                     x = Math.max(this.minX, x);
8798                     break;
8799                 case "west":
8800                     diffX = this.constrain(w, diffX, mw, mxw);
8801                     x += diffX;
8802                     w -= diffX;
8803                     break;
8804                 case "northeast":
8805                     w += diffX;
8806                     w = Math.min(Math.max(mw, w), mxw);
8807                     diffY = this.constrain(h, diffY, mh, mxh);
8808                     y += diffY;
8809                     h -= diffY;
8810                     break;
8811                 case "northwest":
8812                     diffX = this.constrain(w, diffX, mw, mxw);
8813                     diffY = this.constrain(h, diffY, mh, mxh);
8814                     y += diffY;
8815                     h -= diffY;
8816                     x += diffX;
8817                     w -= diffX;
8818                     break;
8819                case "southwest":
8820                     diffX = this.constrain(w, diffX, mw, mxw);
8821                     h += diffY;
8822                     h = Math.min(Math.max(mh, h), mxh);
8823                     x += diffX;
8824                     w -= diffX;
8825                     break;
8826             }
8827
8828             var sw = this.snap(w, wi, mw);
8829             var sh = this.snap(h, hi, mh);
8830             if(sw != w || sh != h){
8831                 switch(pos){
8832                     case "northeast":
8833                         y -= sh - h;
8834                     break;
8835                     case "north":
8836                         y -= sh - h;
8837                         break;
8838                     case "southwest":
8839                         x -= sw - w;
8840                     break;
8841                     case "west":
8842                         x -= sw - w;
8843                         break;
8844                     case "northwest":
8845                         x -= sw - w;
8846                         y -= sh - h;
8847                     break;
8848                 }
8849                 w = sw;
8850                 h = sh;
8851             }
8852
8853             if(this.preserveRatio){
8854                 switch(pos){
8855                     case "southeast":
8856                     case "east":
8857                         h = oh * (w/ow);
8858                         h = Math.min(Math.max(mh, h), mxh);
8859                         w = ow * (h/oh);
8860                        break;
8861                     case "south":
8862                         w = ow * (h/oh);
8863                         w = Math.min(Math.max(mw, w), mxw);
8864                         h = oh * (w/ow);
8865                         break;
8866                     case "northeast":
8867                         w = ow * (h/oh);
8868                         w = Math.min(Math.max(mw, w), mxw);
8869                         h = oh * (w/ow);
8870                     break;
8871                     case "north":
8872                         var tw = w;
8873                         w = ow * (h/oh);
8874                         w = Math.min(Math.max(mw, w), mxw);
8875                         h = oh * (w/ow);
8876                         x += (tw - w) / 2;
8877                         break;
8878                     case "southwest":
8879                         h = oh * (w/ow);
8880                         h = Math.min(Math.max(mh, h), mxh);
8881                         var tw = w;
8882                         w = ow * (h/oh);
8883                         x += tw - w;
8884                         break;
8885                     case "west":
8886                         var th = h;
8887                         h = oh * (w/ow);
8888                         h = Math.min(Math.max(mh, h), mxh);
8889                         y += (th - h) / 2;
8890                         var tw = w;
8891                         w = ow * (h/oh);
8892                         x += tw - w;
8893                        break;
8894                     case "northwest":
8895                         var tw = w;
8896                         var th = h;
8897                         h = oh * (w/ow);
8898                         h = Math.min(Math.max(mh, h), mxh);
8899                         w = ow * (h/oh);
8900                         y += th - h;
8901                         x += tw - w;
8902                        break;
8903
8904                 }
8905             }
8906             if (pos == 'hdrag') {
8907                 w = ow;
8908             }
8909             this.proxy.setBounds(x, y, w, h);
8910             if(this.dynamic){
8911                 this.resizeElement();
8912             }
8913             }catch(e){}
8914         }
8915         this.fireEvent("resizing", this, x, y, w, h, e);
8916     },
8917
8918     // private
8919     handleOver : function(){
8920         if(this.enabled){
8921             this.el.addClass("x-resizable-over");
8922         }
8923     },
8924
8925     // private
8926     handleOut : function(){
8927         if(!this.resizing){
8928             this.el.removeClass("x-resizable-over");
8929         }
8930     },
8931
8932     /**
8933      * Returns the element this component is bound to.
8934      * @return {Roo.Element}
8935      */
8936     getEl : function(){
8937         return this.el;
8938     },
8939
8940     /**
8941      * Returns the resizeChild element (or null).
8942      * @return {Roo.Element}
8943      */
8944     getResizeChild : function(){
8945         return this.resizeChild;
8946     },
8947     groupHandler : function()
8948     {
8949         
8950     },
8951     /**
8952      * Destroys this resizable. If the element was wrapped and
8953      * removeEl is not true then the element remains.
8954      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8955      */
8956     destroy : function(removeEl){
8957         this.proxy.remove();
8958         if(this.overlay){
8959             this.overlay.removeAllListeners();
8960             this.overlay.remove();
8961         }
8962         var ps = Roo.Resizable.positions;
8963         for(var k in ps){
8964             if(typeof ps[k] != "function" && this[ps[k]]){
8965                 var h = this[ps[k]];
8966                 h.el.removeAllListeners();
8967                 h.el.remove();
8968             }
8969         }
8970         if(removeEl){
8971             this.el.update("");
8972             this.el.remove();
8973         }
8974     }
8975 });
8976
8977 // private
8978 // hash to map config positions to true positions
8979 Roo.Resizable.positions = {
8980     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
8981     hd: "hdrag"
8982 };
8983
8984 // private
8985 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8986     if(!this.tpl){
8987         // only initialize the template if resizable is used
8988         var tpl = Roo.DomHelper.createTemplate(
8989             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8990         );
8991         tpl.compile();
8992         Roo.Resizable.Handle.prototype.tpl = tpl;
8993     }
8994     this.position = pos;
8995     this.rz = rz;
8996     // show north drag fro topdra
8997     var handlepos = pos == 'hdrag' ? 'north' : pos;
8998     
8999     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
9000     if (pos == 'hdrag') {
9001         this.el.setStyle('cursor', 'pointer');
9002     }
9003     this.el.unselectable();
9004     if(transparent){
9005         this.el.setOpacity(0);
9006     }
9007     this.el.on("mousedown", this.onMouseDown, this);
9008     if(!disableTrackOver){
9009         this.el.on("mouseover", this.onMouseOver, this);
9010         this.el.on("mouseout", this.onMouseOut, this);
9011     }
9012 };
9013
9014 // private
9015 Roo.Resizable.Handle.prototype = {
9016     afterResize : function(rz){
9017         Roo.log('after?');
9018         // do nothing
9019     },
9020     // private
9021     onMouseDown : function(e){
9022         this.rz.onMouseDown(this, e);
9023     },
9024     // private
9025     onMouseOver : function(e){
9026         this.rz.handleOver(this, e);
9027     },
9028     // private
9029     onMouseOut : function(e){
9030         this.rz.handleOut(this, e);
9031     }
9032 };/*
9033  * Based on:
9034  * Ext JS Library 1.1.1
9035  * Copyright(c) 2006-2007, Ext JS, LLC.
9036  *
9037  * Originally Released Under LGPL - original licence link has changed is not relivant.
9038  *
9039  * Fork - LGPL
9040  * <script type="text/javascript">
9041  */
9042
9043 /**
9044  * @class Roo.Editor
9045  * @extends Roo.Component
9046  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
9047  * @constructor
9048  * Create a new Editor
9049  * @param {Roo.form.Field} field The Field object (or descendant)
9050  * @param {Object} config The config object
9051  */
9052 Roo.Editor = function(field, config){
9053     Roo.Editor.superclass.constructor.call(this, config);
9054     this.field = field;
9055     this.addEvents({
9056         /**
9057              * @event beforestartedit
9058              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
9059              * false from the handler of this event.
9060              * @param {Editor} this
9061              * @param {Roo.Element} boundEl The underlying element bound to this editor
9062              * @param {Mixed} value The field value being set
9063              */
9064         "beforestartedit" : true,
9065         /**
9066              * @event startedit
9067              * Fires when this editor is displayed
9068              * @param {Roo.Element} boundEl The underlying element bound to this editor
9069              * @param {Mixed} value The starting field value
9070              */
9071         "startedit" : true,
9072         /**
9073              * @event beforecomplete
9074              * Fires after a change has been made to the field, but before the change is reflected in the underlying
9075              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
9076              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
9077              * event will not fire since no edit actually occurred.
9078              * @param {Editor} this
9079              * @param {Mixed} value The current field value
9080              * @param {Mixed} startValue The original field value
9081              */
9082         "beforecomplete" : true,
9083         /**
9084              * @event complete
9085              * Fires after editing is complete and any changed value has been written to the underlying field.
9086              * @param {Editor} this
9087              * @param {Mixed} value The current field value
9088              * @param {Mixed} startValue The original field value
9089              */
9090         "complete" : true,
9091         /**
9092          * @event specialkey
9093          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9094          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9095          * @param {Roo.form.Field} this
9096          * @param {Roo.EventObject} e The event object
9097          */
9098         "specialkey" : true
9099     });
9100 };
9101
9102 Roo.extend(Roo.Editor, Roo.Component, {
9103     /**
9104      * @cfg {Boolean/String} autosize
9105      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
9106      * or "height" to adopt the height only (defaults to false)
9107      */
9108     /**
9109      * @cfg {Boolean} revertInvalid
9110      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
9111      * validation fails (defaults to true)
9112      */
9113     /**
9114      * @cfg {Boolean} ignoreNoChange
9115      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
9116      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
9117      * will never be ignored.
9118      */
9119     /**
9120      * @cfg {Boolean} hideEl
9121      * False to keep the bound element visible while the editor is displayed (defaults to true)
9122      */
9123     /**
9124      * @cfg {Mixed} value
9125      * The data value of the underlying field (defaults to "")
9126      */
9127     value : "",
9128     /**
9129      * @cfg {String} alignment
9130      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
9131      */
9132     alignment: "c-c?",
9133     /**
9134      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
9135      * for bottom-right shadow (defaults to "frame")
9136      */
9137     shadow : "frame",
9138     /**
9139      * @cfg {Boolean} constrain True to constrain the editor to the viewport
9140      */
9141     constrain : false,
9142     /**
9143      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
9144      */
9145     completeOnEnter : false,
9146     /**
9147      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
9148      */
9149     cancelOnEsc : false,
9150     /**
9151      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
9152      */
9153     updateEl : false,
9154
9155     // private
9156     onRender : function(ct, position){
9157         this.el = new Roo.Layer({
9158             shadow: this.shadow,
9159             cls: "x-editor",
9160             parentEl : ct,
9161             shim : this.shim,
9162             shadowOffset:4,
9163             id: this.id,
9164             constrain: this.constrain
9165         });
9166         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
9167         if(this.field.msgTarget != 'title'){
9168             this.field.msgTarget = 'qtip';
9169         }
9170         this.field.render(this.el);
9171         if(Roo.isGecko){
9172             this.field.el.dom.setAttribute('autocomplete', 'off');
9173         }
9174         this.field.on("specialkey", this.onSpecialKey, this);
9175         if(this.swallowKeys){
9176             this.field.el.swallowEvent(['keydown','keypress']);
9177         }
9178         this.field.show();
9179         this.field.on("blur", this.onBlur, this);
9180         if(this.field.grow){
9181             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
9182         }
9183     },
9184
9185     onSpecialKey : function(field, e)
9186     {
9187         //Roo.log('editor onSpecialKey');
9188         if(this.completeOnEnter && e.getKey() == e.ENTER){
9189             e.stopEvent();
9190             this.completeEdit();
9191             return;
9192         }
9193         // do not fire special key otherwise it might hide close the editor...
9194         if(e.getKey() == e.ENTER){    
9195             return;
9196         }
9197         if(this.cancelOnEsc && e.getKey() == e.ESC){
9198             this.cancelEdit();
9199             return;
9200         } 
9201         this.fireEvent('specialkey', field, e);
9202     
9203     },
9204
9205     /**
9206      * Starts the editing process and shows the editor.
9207      * @param {String/HTMLElement/Element} el The element to edit
9208      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
9209       * to the innerHTML of el.
9210      */
9211     startEdit : function(el, value){
9212         if(this.editing){
9213             this.completeEdit();
9214         }
9215         this.boundEl = Roo.get(el);
9216         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
9217         if(!this.rendered){
9218             this.render(this.parentEl || document.body);
9219         }
9220         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
9221             return;
9222         }
9223         this.startValue = v;
9224         this.field.setValue(v);
9225         if(this.autoSize){
9226             var sz = this.boundEl.getSize();
9227             switch(this.autoSize){
9228                 case "width":
9229                 this.setSize(sz.width,  "");
9230                 break;
9231                 case "height":
9232                 this.setSize("",  sz.height);
9233                 break;
9234                 default:
9235                 this.setSize(sz.width,  sz.height);
9236             }
9237         }
9238         this.el.alignTo(this.boundEl, this.alignment);
9239         this.editing = true;
9240         if(Roo.QuickTips){
9241             Roo.QuickTips.disable();
9242         }
9243         this.show();
9244     },
9245
9246     /**
9247      * Sets the height and width of this editor.
9248      * @param {Number} width The new width
9249      * @param {Number} height The new height
9250      */
9251     setSize : function(w, h){
9252         this.field.setSize(w, h);
9253         if(this.el){
9254             this.el.sync();
9255         }
9256     },
9257
9258     /**
9259      * Realigns the editor to the bound field based on the current alignment config value.
9260      */
9261     realign : function(){
9262         this.el.alignTo(this.boundEl, this.alignment);
9263     },
9264
9265     /**
9266      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
9267      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
9268      */
9269     completeEdit : function(remainVisible){
9270         if(!this.editing){
9271             return;
9272         }
9273         var v = this.getValue();
9274         if(this.revertInvalid !== false && !this.field.isValid()){
9275             v = this.startValue;
9276             this.cancelEdit(true);
9277         }
9278         if(String(v) === String(this.startValue) && this.ignoreNoChange){
9279             this.editing = false;
9280             this.hide();
9281             return;
9282         }
9283         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
9284             this.editing = false;
9285             if(this.updateEl && this.boundEl){
9286                 this.boundEl.update(v);
9287             }
9288             if(remainVisible !== true){
9289                 this.hide();
9290             }
9291             this.fireEvent("complete", this, v, this.startValue);
9292         }
9293     },
9294
9295     // private
9296     onShow : function(){
9297         this.el.show();
9298         if(this.hideEl !== false){
9299             this.boundEl.hide();
9300         }
9301         this.field.show();
9302         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
9303             this.fixIEFocus = true;
9304             this.deferredFocus.defer(50, this);
9305         }else{
9306             this.field.focus();
9307         }
9308         this.fireEvent("startedit", this.boundEl, this.startValue);
9309     },
9310
9311     deferredFocus : function(){
9312         if(this.editing){
9313             this.field.focus();
9314         }
9315     },
9316
9317     /**
9318      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
9319      * reverted to the original starting value.
9320      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
9321      * cancel (defaults to false)
9322      */
9323     cancelEdit : function(remainVisible){
9324         if(this.editing){
9325             this.setValue(this.startValue);
9326             if(remainVisible !== true){
9327                 this.hide();
9328             }
9329         }
9330     },
9331
9332     // private
9333     onBlur : function(){
9334         if(this.allowBlur !== true && this.editing){
9335             this.completeEdit();
9336         }
9337     },
9338
9339     // private
9340     onHide : function(){
9341         if(this.editing){
9342             this.completeEdit();
9343             return;
9344         }
9345         this.field.blur();
9346         if(this.field.collapse){
9347             this.field.collapse();
9348         }
9349         this.el.hide();
9350         if(this.hideEl !== false){
9351             this.boundEl.show();
9352         }
9353         if(Roo.QuickTips){
9354             Roo.QuickTips.enable();
9355         }
9356     },
9357
9358     /**
9359      * Sets the data value of the editor
9360      * @param {Mixed} value Any valid value supported by the underlying field
9361      */
9362     setValue : function(v){
9363         this.field.setValue(v);
9364     },
9365
9366     /**
9367      * Gets the data value of the editor
9368      * @return {Mixed} The data value
9369      */
9370     getValue : function(){
9371         return this.field.getValue();
9372     }
9373 });/*
9374  * Based on:
9375  * Ext JS Library 1.1.1
9376  * Copyright(c) 2006-2007, Ext JS, LLC.
9377  *
9378  * Originally Released Under LGPL - original licence link has changed is not relivant.
9379  *
9380  * Fork - LGPL
9381  * <script type="text/javascript">
9382  */
9383  
9384 /**
9385  * @class Roo.BasicDialog
9386  * @extends Roo.util.Observable
9387  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
9388  * <pre><code>
9389 var dlg = new Roo.BasicDialog("my-dlg", {
9390     height: 200,
9391     width: 300,
9392     minHeight: 100,
9393     minWidth: 150,
9394     modal: true,
9395     proxyDrag: true,
9396     shadow: true
9397 });
9398 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9399 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
9400 dlg.addButton('Cancel', dlg.hide, dlg);
9401 dlg.show();
9402 </code></pre>
9403   <b>A Dialog should always be a direct child of the body element.</b>
9404  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9405  * @cfg {String} title Default text to display in the title bar (defaults to null)
9406  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9407  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9408  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9409  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9410  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9411  * (defaults to null with no animation)
9412  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9413  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9414  * property for valid values (defaults to 'all')
9415  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9416  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9417  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9418  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9419  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9420  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9421  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9422  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9423  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9424  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9425  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9426  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9427  * draggable = true (defaults to false)
9428  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9429  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9430  * shadow (defaults to false)
9431  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9432  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9433  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9434  * @cfg {Array} buttons Array of buttons
9435  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9436  * @constructor
9437  * Create a new BasicDialog.
9438  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9439  * @param {Object} config Configuration options
9440  */
9441 Roo.BasicDialog = function(el, config){
9442     this.el = Roo.get(el);
9443     var dh = Roo.DomHelper;
9444     if(!this.el && config && config.autoCreate){
9445         if(typeof config.autoCreate == "object"){
9446             if(!config.autoCreate.id){
9447                 config.autoCreate.id = el;
9448             }
9449             this.el = dh.append(document.body,
9450                         config.autoCreate, true);
9451         }else{
9452             this.el = dh.append(document.body,
9453                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9454         }
9455     }
9456     el = this.el;
9457     el.setDisplayed(true);
9458     el.hide = this.hideAction;
9459     this.id = el.id;
9460     el.addClass("x-dlg");
9461
9462     Roo.apply(this, config);
9463
9464     this.proxy = el.createProxy("x-dlg-proxy");
9465     this.proxy.hide = this.hideAction;
9466     this.proxy.setOpacity(.5);
9467     this.proxy.hide();
9468
9469     if(config.width){
9470         el.setWidth(config.width);
9471     }
9472     if(config.height){
9473         el.setHeight(config.height);
9474     }
9475     this.size = el.getSize();
9476     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9477         this.xy = [config.x,config.y];
9478     }else{
9479         this.xy = el.getCenterXY(true);
9480     }
9481     /** The header element @type Roo.Element */
9482     this.header = el.child("> .x-dlg-hd");
9483     /** The body element @type Roo.Element */
9484     this.body = el.child("> .x-dlg-bd");
9485     /** The footer element @type Roo.Element */
9486     this.footer = el.child("> .x-dlg-ft");
9487
9488     if(!this.header){
9489         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9490     }
9491     if(!this.body){
9492         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9493     }
9494
9495     this.header.unselectable();
9496     if(this.title){
9497         this.header.update(this.title);
9498     }
9499     // this element allows the dialog to be focused for keyboard event
9500     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9501     this.focusEl.swallowEvent("click", true);
9502
9503     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9504
9505     // wrap the body and footer for special rendering
9506     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9507     if(this.footer){
9508         this.bwrap.dom.appendChild(this.footer.dom);
9509     }
9510
9511     this.bg = this.el.createChild({
9512         tag: "div", cls:"x-dlg-bg",
9513         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9514     });
9515     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9516
9517
9518     if(this.autoScroll !== false && !this.autoTabs){
9519         this.body.setStyle("overflow", "auto");
9520     }
9521
9522     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9523
9524     if(this.closable !== false){
9525         this.el.addClass("x-dlg-closable");
9526         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9527         this.close.on("click", this.closeClick, this);
9528         this.close.addClassOnOver("x-dlg-close-over");
9529     }
9530     if(this.collapsible !== false){
9531         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9532         this.collapseBtn.on("click", this.collapseClick, this);
9533         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9534         this.header.on("dblclick", this.collapseClick, this);
9535     }
9536     if(this.resizable !== false){
9537         this.el.addClass("x-dlg-resizable");
9538         this.resizer = new Roo.Resizable(el, {
9539             minWidth: this.minWidth || 80,
9540             minHeight:this.minHeight || 80,
9541             handles: this.resizeHandles || "all",
9542             pinned: true
9543         });
9544         this.resizer.on("beforeresize", this.beforeResize, this);
9545         this.resizer.on("resize", this.onResize, this);
9546     }
9547     if(this.draggable !== false){
9548         el.addClass("x-dlg-draggable");
9549         if (!this.proxyDrag) {
9550             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9551         }
9552         else {
9553             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9554         }
9555         dd.setHandleElId(this.header.id);
9556         dd.endDrag = this.endMove.createDelegate(this);
9557         dd.startDrag = this.startMove.createDelegate(this);
9558         dd.onDrag = this.onDrag.createDelegate(this);
9559         dd.scroll = false;
9560         this.dd = dd;
9561     }
9562     if(this.modal){
9563         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9564         this.mask.enableDisplayMode("block");
9565         this.mask.hide();
9566         this.el.addClass("x-dlg-modal");
9567     }
9568     if(this.shadow){
9569         this.shadow = new Roo.Shadow({
9570             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9571             offset : this.shadowOffset
9572         });
9573     }else{
9574         this.shadowOffset = 0;
9575     }
9576     if(Roo.useShims && this.shim !== false){
9577         this.shim = this.el.createShim();
9578         this.shim.hide = this.hideAction;
9579         this.shim.hide();
9580     }else{
9581         this.shim = false;
9582     }
9583     if(this.autoTabs){
9584         this.initTabs();
9585     }
9586     if (this.buttons) { 
9587         var bts= this.buttons;
9588         this.buttons = [];
9589         Roo.each(bts, function(b) {
9590             this.addButton(b);
9591         }, this);
9592     }
9593     
9594     
9595     this.addEvents({
9596         /**
9597          * @event keydown
9598          * Fires when a key is pressed
9599          * @param {Roo.BasicDialog} this
9600          * @param {Roo.EventObject} e
9601          */
9602         "keydown" : true,
9603         /**
9604          * @event move
9605          * Fires when this dialog is moved by the user.
9606          * @param {Roo.BasicDialog} this
9607          * @param {Number} x The new page X
9608          * @param {Number} y The new page Y
9609          */
9610         "move" : true,
9611         /**
9612          * @event resize
9613          * Fires when this dialog is resized by the user.
9614          * @param {Roo.BasicDialog} this
9615          * @param {Number} width The new width
9616          * @param {Number} height The new height
9617          */
9618         "resize" : true,
9619         /**
9620          * @event beforehide
9621          * Fires before this dialog is hidden.
9622          * @param {Roo.BasicDialog} this
9623          */
9624         "beforehide" : true,
9625         /**
9626          * @event hide
9627          * Fires when this dialog is hidden.
9628          * @param {Roo.BasicDialog} this
9629          */
9630         "hide" : true,
9631         /**
9632          * @event beforeshow
9633          * Fires before this dialog is shown.
9634          * @param {Roo.BasicDialog} this
9635          */
9636         "beforeshow" : true,
9637         /**
9638          * @event show
9639          * Fires when this dialog is shown.
9640          * @param {Roo.BasicDialog} this
9641          */
9642         "show" : true
9643     });
9644     el.on("keydown", this.onKeyDown, this);
9645     el.on("mousedown", this.toFront, this);
9646     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9647     this.el.hide();
9648     Roo.DialogManager.register(this);
9649     Roo.BasicDialog.superclass.constructor.call(this);
9650 };
9651
9652 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9653     shadowOffset: Roo.isIE ? 6 : 5,
9654     minHeight: 80,
9655     minWidth: 200,
9656     minButtonWidth: 75,
9657     defaultButton: null,
9658     buttonAlign: "right",
9659     tabTag: 'div',
9660     firstShow: true,
9661
9662     /**
9663      * Sets the dialog title text
9664      * @param {String} text The title text to display
9665      * @return {Roo.BasicDialog} this
9666      */
9667     setTitle : function(text){
9668         this.header.update(text);
9669         return this;
9670     },
9671
9672     // private
9673     closeClick : function(){
9674         this.hide();
9675     },
9676
9677     // private
9678     collapseClick : function(){
9679         this[this.collapsed ? "expand" : "collapse"]();
9680     },
9681
9682     /**
9683      * Collapses the dialog to its minimized state (only the title bar is visible).
9684      * Equivalent to the user clicking the collapse dialog button.
9685      */
9686     collapse : function(){
9687         if(!this.collapsed){
9688             this.collapsed = true;
9689             this.el.addClass("x-dlg-collapsed");
9690             this.restoreHeight = this.el.getHeight();
9691             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9692         }
9693     },
9694
9695     /**
9696      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9697      * clicking the expand dialog button.
9698      */
9699     expand : function(){
9700         if(this.collapsed){
9701             this.collapsed = false;
9702             this.el.removeClass("x-dlg-collapsed");
9703             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9704         }
9705     },
9706
9707     /**
9708      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9709      * @return {Roo.TabPanel} The tabs component
9710      */
9711     initTabs : function(){
9712         var tabs = this.getTabs();
9713         while(tabs.getTab(0)){
9714             tabs.removeTab(0);
9715         }
9716         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9717             var dom = el.dom;
9718             tabs.addTab(Roo.id(dom), dom.title);
9719             dom.title = "";
9720         });
9721         tabs.activate(0);
9722         return tabs;
9723     },
9724
9725     // private
9726     beforeResize : function(){
9727         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9728     },
9729
9730     // private
9731     onResize : function(){
9732         this.refreshSize();
9733         this.syncBodyHeight();
9734         this.adjustAssets();
9735         this.focus();
9736         this.fireEvent("resize", this, this.size.width, this.size.height);
9737     },
9738
9739     // private
9740     onKeyDown : function(e){
9741         if(this.isVisible()){
9742             this.fireEvent("keydown", this, e);
9743         }
9744     },
9745
9746     /**
9747      * Resizes the dialog.
9748      * @param {Number} width
9749      * @param {Number} height
9750      * @return {Roo.BasicDialog} this
9751      */
9752     resizeTo : function(width, height){
9753         this.el.setSize(width, height);
9754         this.size = {width: width, height: height};
9755         this.syncBodyHeight();
9756         if(this.fixedcenter){
9757             this.center();
9758         }
9759         if(this.isVisible()){
9760             this.constrainXY();
9761             this.adjustAssets();
9762         }
9763         this.fireEvent("resize", this, width, height);
9764         return this;
9765     },
9766
9767
9768     /**
9769      * Resizes the dialog to fit the specified content size.
9770      * @param {Number} width
9771      * @param {Number} height
9772      * @return {Roo.BasicDialog} this
9773      */
9774     setContentSize : function(w, h){
9775         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9776         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9777         //if(!this.el.isBorderBox()){
9778             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9779             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9780         //}
9781         if(this.tabs){
9782             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9783             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9784         }
9785         this.resizeTo(w, h);
9786         return this;
9787     },
9788
9789     /**
9790      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9791      * executed in response to a particular key being pressed while the dialog is active.
9792      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9793      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9794      * @param {Function} fn The function to call
9795      * @param {Object} scope (optional) The scope of the function
9796      * @return {Roo.BasicDialog} this
9797      */
9798     addKeyListener : function(key, fn, scope){
9799         var keyCode, shift, ctrl, alt;
9800         if(typeof key == "object" && !(key instanceof Array)){
9801             keyCode = key["key"];
9802             shift = key["shift"];
9803             ctrl = key["ctrl"];
9804             alt = key["alt"];
9805         }else{
9806             keyCode = key;
9807         }
9808         var handler = function(dlg, e){
9809             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9810                 var k = e.getKey();
9811                 if(keyCode instanceof Array){
9812                     for(var i = 0, len = keyCode.length; i < len; i++){
9813                         if(keyCode[i] == k){
9814                           fn.call(scope || window, dlg, k, e);
9815                           return;
9816                         }
9817                     }
9818                 }else{
9819                     if(k == keyCode){
9820                         fn.call(scope || window, dlg, k, e);
9821                     }
9822                 }
9823             }
9824         };
9825         this.on("keydown", handler);
9826         return this;
9827     },
9828
9829     /**
9830      * Returns the TabPanel component (creates it if it doesn't exist).
9831      * Note: If you wish to simply check for the existence of tabs without creating them,
9832      * check for a null 'tabs' property.
9833      * @return {Roo.TabPanel} The tabs component
9834      */
9835     getTabs : function(){
9836         if(!this.tabs){
9837             this.el.addClass("x-dlg-auto-tabs");
9838             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9839             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9840         }
9841         return this.tabs;
9842     },
9843
9844     /**
9845      * Adds a button to the footer section of the dialog.
9846      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9847      * object or a valid Roo.DomHelper element config
9848      * @param {Function} handler The function called when the button is clicked
9849      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9850      * @return {Roo.Button} The new button
9851      */
9852     addButton : function(config, handler, scope){
9853         var dh = Roo.DomHelper;
9854         if(!this.footer){
9855             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9856         }
9857         if(!this.btnContainer){
9858             var tb = this.footer.createChild({
9859
9860                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9861                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9862             }, null, true);
9863             this.btnContainer = tb.firstChild.firstChild.firstChild;
9864         }
9865         var bconfig = {
9866             handler: handler,
9867             scope: scope,
9868             minWidth: this.minButtonWidth,
9869             hideParent:true
9870         };
9871         if(typeof config == "string"){
9872             bconfig.text = config;
9873         }else{
9874             if(config.tag){
9875                 bconfig.dhconfig = config;
9876             }else{
9877                 Roo.apply(bconfig, config);
9878             }
9879         }
9880         var fc = false;
9881         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9882             bconfig.position = Math.max(0, bconfig.position);
9883             fc = this.btnContainer.childNodes[bconfig.position];
9884         }
9885          
9886         var btn = new Roo.Button(
9887             fc ? 
9888                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9889                 : this.btnContainer.appendChild(document.createElement("td")),
9890             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9891             bconfig
9892         );
9893         this.syncBodyHeight();
9894         if(!this.buttons){
9895             /**
9896              * Array of all the buttons that have been added to this dialog via addButton
9897              * @type Array
9898              */
9899             this.buttons = [];
9900         }
9901         this.buttons.push(btn);
9902         return btn;
9903     },
9904
9905     /**
9906      * Sets the default button to be focused when the dialog is displayed.
9907      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9908      * @return {Roo.BasicDialog} this
9909      */
9910     setDefaultButton : function(btn){
9911         this.defaultButton = btn;
9912         return this;
9913     },
9914
9915     // private
9916     getHeaderFooterHeight : function(safe){
9917         var height = 0;
9918         if(this.header){
9919            height += this.header.getHeight();
9920         }
9921         if(this.footer){
9922            var fm = this.footer.getMargins();
9923             height += (this.footer.getHeight()+fm.top+fm.bottom);
9924         }
9925         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9926         height += this.centerBg.getPadding("tb");
9927         return height;
9928     },
9929
9930     // private
9931     syncBodyHeight : function()
9932     {
9933         var bd = this.body, // the text
9934             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9935             bw = this.bwrap;
9936         var height = this.size.height - this.getHeaderFooterHeight(false);
9937         bd.setHeight(height-bd.getMargins("tb"));
9938         var hh = this.header.getHeight();
9939         var h = this.size.height-hh;
9940         cb.setHeight(h);
9941         
9942         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9943         bw.setHeight(h-cb.getPadding("tb"));
9944         
9945         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9946         bd.setWidth(bw.getWidth(true));
9947         if(this.tabs){
9948             this.tabs.syncHeight();
9949             if(Roo.isIE){
9950                 this.tabs.el.repaint();
9951             }
9952         }
9953     },
9954
9955     /**
9956      * Restores the previous state of the dialog if Roo.state is configured.
9957      * @return {Roo.BasicDialog} this
9958      */
9959     restoreState : function(){
9960         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9961         if(box && box.width){
9962             this.xy = [box.x, box.y];
9963             this.resizeTo(box.width, box.height);
9964         }
9965         return this;
9966     },
9967
9968     // private
9969     beforeShow : function(){
9970         this.expand();
9971         if(this.fixedcenter){
9972             this.xy = this.el.getCenterXY(true);
9973         }
9974         if(this.modal){
9975             Roo.get(document.body).addClass("x-body-masked");
9976             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9977             this.mask.show();
9978         }
9979         this.constrainXY();
9980     },
9981
9982     // private
9983     animShow : function(){
9984         var b = Roo.get(this.animateTarget).getBox();
9985         this.proxy.setSize(b.width, b.height);
9986         this.proxy.setLocation(b.x, b.y);
9987         this.proxy.show();
9988         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9989                     true, .35, this.showEl.createDelegate(this));
9990     },
9991
9992     /**
9993      * Shows the dialog.
9994      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
9995      * @return {Roo.BasicDialog} this
9996      */
9997     show : function(animateTarget){
9998         if (this.fireEvent("beforeshow", this) === false){
9999             return;
10000         }
10001         if(this.syncHeightBeforeShow){
10002             this.syncBodyHeight();
10003         }else if(this.firstShow){
10004             this.firstShow = false;
10005             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
10006         }
10007         this.animateTarget = animateTarget || this.animateTarget;
10008         if(!this.el.isVisible()){
10009             this.beforeShow();
10010             if(this.animateTarget && Roo.get(this.animateTarget)){
10011                 this.animShow();
10012             }else{
10013                 this.showEl();
10014             }
10015         }
10016         return this;
10017     },
10018
10019     // private
10020     showEl : function(){
10021         this.proxy.hide();
10022         this.el.setXY(this.xy);
10023         this.el.show();
10024         this.adjustAssets(true);
10025         this.toFront();
10026         this.focus();
10027         // IE peekaboo bug - fix found by Dave Fenwick
10028         if(Roo.isIE){
10029             this.el.repaint();
10030         }
10031         this.fireEvent("show", this);
10032     },
10033
10034     /**
10035      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
10036      * dialog itself will receive focus.
10037      */
10038     focus : function(){
10039         if(this.defaultButton){
10040             this.defaultButton.focus();
10041         }else{
10042             this.focusEl.focus();
10043         }
10044     },
10045
10046     // private
10047     constrainXY : function(){
10048         if(this.constraintoviewport !== false){
10049             if(!this.viewSize){
10050                 if(this.container){
10051                     var s = this.container.getSize();
10052                     this.viewSize = [s.width, s.height];
10053                 }else{
10054                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
10055                 }
10056             }
10057             var s = Roo.get(this.container||document).getScroll();
10058
10059             var x = this.xy[0], y = this.xy[1];
10060             var w = this.size.width, h = this.size.height;
10061             var vw = this.viewSize[0], vh = this.viewSize[1];
10062             // only move it if it needs it
10063             var moved = false;
10064             // first validate right/bottom
10065             if(x + w > vw+s.left){
10066                 x = vw - w;
10067                 moved = true;
10068             }
10069             if(y + h > vh+s.top){
10070                 y = vh - h;
10071                 moved = true;
10072             }
10073             // then make sure top/left isn't negative
10074             if(x < s.left){
10075                 x = s.left;
10076                 moved = true;
10077             }
10078             if(y < s.top){
10079                 y = s.top;
10080                 moved = true;
10081             }
10082             if(moved){
10083                 // cache xy
10084                 this.xy = [x, y];
10085                 if(this.isVisible()){
10086                     this.el.setLocation(x, y);
10087                     this.adjustAssets();
10088                 }
10089             }
10090         }
10091     },
10092
10093     // private
10094     onDrag : function(){
10095         if(!this.proxyDrag){
10096             this.xy = this.el.getXY();
10097             this.adjustAssets();
10098         }
10099     },
10100
10101     // private
10102     adjustAssets : function(doShow){
10103         var x = this.xy[0], y = this.xy[1];
10104         var w = this.size.width, h = this.size.height;
10105         if(doShow === true){
10106             if(this.shadow){
10107                 this.shadow.show(this.el);
10108             }
10109             if(this.shim){
10110                 this.shim.show();
10111             }
10112         }
10113         if(this.shadow && this.shadow.isVisible()){
10114             this.shadow.show(this.el);
10115         }
10116         if(this.shim && this.shim.isVisible()){
10117             this.shim.setBounds(x, y, w, h);
10118         }
10119     },
10120
10121     // private
10122     adjustViewport : function(w, h){
10123         if(!w || !h){
10124             w = Roo.lib.Dom.getViewWidth();
10125             h = Roo.lib.Dom.getViewHeight();
10126         }
10127         // cache the size
10128         this.viewSize = [w, h];
10129         if(this.modal && this.mask.isVisible()){
10130             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
10131             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
10132         }
10133         if(this.isVisible()){
10134             this.constrainXY();
10135         }
10136     },
10137
10138     /**
10139      * Destroys this dialog and all its supporting elements (including any tabs, shim,
10140      * shadow, proxy, mask, etc.)  Also removes all event listeners.
10141      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
10142      */
10143     destroy : function(removeEl){
10144         if(this.isVisible()){
10145             this.animateTarget = null;
10146             this.hide();
10147         }
10148         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
10149         if(this.tabs){
10150             this.tabs.destroy(removeEl);
10151         }
10152         Roo.destroy(
10153              this.shim,
10154              this.proxy,
10155              this.resizer,
10156              this.close,
10157              this.mask
10158         );
10159         if(this.dd){
10160             this.dd.unreg();
10161         }
10162         if(this.buttons){
10163            for(var i = 0, len = this.buttons.length; i < len; i++){
10164                this.buttons[i].destroy();
10165            }
10166         }
10167         this.el.removeAllListeners();
10168         if(removeEl === true){
10169             this.el.update("");
10170             this.el.remove();
10171         }
10172         Roo.DialogManager.unregister(this);
10173     },
10174
10175     // private
10176     startMove : function(){
10177         if(this.proxyDrag){
10178             this.proxy.show();
10179         }
10180         if(this.constraintoviewport !== false){
10181             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
10182         }
10183     },
10184
10185     // private
10186     endMove : function(){
10187         if(!this.proxyDrag){
10188             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
10189         }else{
10190             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
10191             this.proxy.hide();
10192         }
10193         this.refreshSize();
10194         this.adjustAssets();
10195         this.focus();
10196         this.fireEvent("move", this, this.xy[0], this.xy[1]);
10197     },
10198
10199     /**
10200      * Brings this dialog to the front of any other visible dialogs
10201      * @return {Roo.BasicDialog} this
10202      */
10203     toFront : function(){
10204         Roo.DialogManager.bringToFront(this);
10205         return this;
10206     },
10207
10208     /**
10209      * Sends this dialog to the back (under) of any other visible dialogs
10210      * @return {Roo.BasicDialog} this
10211      */
10212     toBack : function(){
10213         Roo.DialogManager.sendToBack(this);
10214         return this;
10215     },
10216
10217     /**
10218      * Centers this dialog in the viewport
10219      * @return {Roo.BasicDialog} this
10220      */
10221     center : function(){
10222         var xy = this.el.getCenterXY(true);
10223         this.moveTo(xy[0], xy[1]);
10224         return this;
10225     },
10226
10227     /**
10228      * Moves the dialog's top-left corner to the specified point
10229      * @param {Number} x
10230      * @param {Number} y
10231      * @return {Roo.BasicDialog} this
10232      */
10233     moveTo : function(x, y){
10234         this.xy = [x,y];
10235         if(this.isVisible()){
10236             this.el.setXY(this.xy);
10237             this.adjustAssets();
10238         }
10239         return this;
10240     },
10241
10242     /**
10243      * Aligns the dialog to the specified element
10244      * @param {String/HTMLElement/Roo.Element} element The element to align to.
10245      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
10246      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10247      * @return {Roo.BasicDialog} this
10248      */
10249     alignTo : function(element, position, offsets){
10250         this.xy = this.el.getAlignToXY(element, position, offsets);
10251         if(this.isVisible()){
10252             this.el.setXY(this.xy);
10253             this.adjustAssets();
10254         }
10255         return this;
10256     },
10257
10258     /**
10259      * Anchors an element to another element and realigns it when the window is resized.
10260      * @param {String/HTMLElement/Roo.Element} element The element to align to.
10261      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
10262      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10263      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
10264      * is a number, it is used as the buffer delay (defaults to 50ms).
10265      * @return {Roo.BasicDialog} this
10266      */
10267     anchorTo : function(el, alignment, offsets, monitorScroll){
10268         var action = function(){
10269             this.alignTo(el, alignment, offsets);
10270         };
10271         Roo.EventManager.onWindowResize(action, this);
10272         var tm = typeof monitorScroll;
10273         if(tm != 'undefined'){
10274             Roo.EventManager.on(window, 'scroll', action, this,
10275                 {buffer: tm == 'number' ? monitorScroll : 50});
10276         }
10277         action.call(this);
10278         return this;
10279     },
10280
10281     /**
10282      * Returns true if the dialog is visible
10283      * @return {Boolean}
10284      */
10285     isVisible : function(){
10286         return this.el.isVisible();
10287     },
10288
10289     // private
10290     animHide : function(callback){
10291         var b = Roo.get(this.animateTarget).getBox();
10292         this.proxy.show();
10293         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
10294         this.el.hide();
10295         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
10296                     this.hideEl.createDelegate(this, [callback]));
10297     },
10298
10299     /**
10300      * Hides the dialog.
10301      * @param {Function} callback (optional) Function to call when the dialog is hidden
10302      * @return {Roo.BasicDialog} this
10303      */
10304     hide : function(callback){
10305         if (this.fireEvent("beforehide", this) === false){
10306             return;
10307         }
10308         if(this.shadow){
10309             this.shadow.hide();
10310         }
10311         if(this.shim) {
10312           this.shim.hide();
10313         }
10314         // sometimes animateTarget seems to get set.. causing problems...
10315         // this just double checks..
10316         if(this.animateTarget && Roo.get(this.animateTarget)) {
10317            this.animHide(callback);
10318         }else{
10319             this.el.hide();
10320             this.hideEl(callback);
10321         }
10322         return this;
10323     },
10324
10325     // private
10326     hideEl : function(callback){
10327         this.proxy.hide();
10328         if(this.modal){
10329             this.mask.hide();
10330             Roo.get(document.body).removeClass("x-body-masked");
10331         }
10332         this.fireEvent("hide", this);
10333         if(typeof callback == "function"){
10334             callback();
10335         }
10336     },
10337
10338     // private
10339     hideAction : function(){
10340         this.setLeft("-10000px");
10341         this.setTop("-10000px");
10342         this.setStyle("visibility", "hidden");
10343     },
10344
10345     // private
10346     refreshSize : function(){
10347         this.size = this.el.getSize();
10348         this.xy = this.el.getXY();
10349         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
10350     },
10351
10352     // private
10353     // z-index is managed by the DialogManager and may be overwritten at any time
10354     setZIndex : function(index){
10355         if(this.modal){
10356             this.mask.setStyle("z-index", index);
10357         }
10358         if(this.shim){
10359             this.shim.setStyle("z-index", ++index);
10360         }
10361         if(this.shadow){
10362             this.shadow.setZIndex(++index);
10363         }
10364         this.el.setStyle("z-index", ++index);
10365         if(this.proxy){
10366             this.proxy.setStyle("z-index", ++index);
10367         }
10368         if(this.resizer){
10369             this.resizer.proxy.setStyle("z-index", ++index);
10370         }
10371
10372         this.lastZIndex = index;
10373     },
10374
10375     /**
10376      * Returns the element for this dialog
10377      * @return {Roo.Element} The underlying dialog Element
10378      */
10379     getEl : function(){
10380         return this.el;
10381     }
10382 });
10383
10384 /**
10385  * @class Roo.DialogManager
10386  * Provides global access to BasicDialogs that have been created and
10387  * support for z-indexing (layering) multiple open dialogs.
10388  */
10389 Roo.DialogManager = function(){
10390     var list = {};
10391     var accessList = [];
10392     var front = null;
10393
10394     // private
10395     var sortDialogs = function(d1, d2){
10396         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10397     };
10398
10399     // private
10400     var orderDialogs = function(){
10401         accessList.sort(sortDialogs);
10402         var seed = Roo.DialogManager.zseed;
10403         for(var i = 0, len = accessList.length; i < len; i++){
10404             var dlg = accessList[i];
10405             if(dlg){
10406                 dlg.setZIndex(seed + (i*10));
10407             }
10408         }
10409     };
10410
10411     return {
10412         /**
10413          * The starting z-index for BasicDialogs (defaults to 9000)
10414          * @type Number The z-index value
10415          */
10416         zseed : 9000,
10417
10418         // private
10419         register : function(dlg){
10420             list[dlg.id] = dlg;
10421             accessList.push(dlg);
10422         },
10423
10424         // private
10425         unregister : function(dlg){
10426             delete list[dlg.id];
10427             var i=0;
10428             var len=0;
10429             if(!accessList.indexOf){
10430                 for(  i = 0, len = accessList.length; i < len; i++){
10431                     if(accessList[i] == dlg){
10432                         accessList.splice(i, 1);
10433                         return;
10434                     }
10435                 }
10436             }else{
10437                  i = accessList.indexOf(dlg);
10438                 if(i != -1){
10439                     accessList.splice(i, 1);
10440                 }
10441             }
10442         },
10443
10444         /**
10445          * Gets a registered dialog by id
10446          * @param {String/Object} id The id of the dialog or a dialog
10447          * @return {Roo.BasicDialog} this
10448          */
10449         get : function(id){
10450             return typeof id == "object" ? id : list[id];
10451         },
10452
10453         /**
10454          * Brings the specified dialog to the front
10455          * @param {String/Object} dlg The id of the dialog or a dialog
10456          * @return {Roo.BasicDialog} this
10457          */
10458         bringToFront : function(dlg){
10459             dlg = this.get(dlg);
10460             if(dlg != front){
10461                 front = dlg;
10462                 dlg._lastAccess = new Date().getTime();
10463                 orderDialogs();
10464             }
10465             return dlg;
10466         },
10467
10468         /**
10469          * Sends the specified dialog to the back
10470          * @param {String/Object} dlg The id of the dialog or a dialog
10471          * @return {Roo.BasicDialog} this
10472          */
10473         sendToBack : function(dlg){
10474             dlg = this.get(dlg);
10475             dlg._lastAccess = -(new Date().getTime());
10476             orderDialogs();
10477             return dlg;
10478         },
10479
10480         /**
10481          * Hides all dialogs
10482          */
10483         hideAll : function(){
10484             for(var id in list){
10485                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10486                     list[id].hide();
10487                 }
10488             }
10489         }
10490     };
10491 }();
10492
10493 /**
10494  * @class Roo.LayoutDialog
10495  * @extends Roo.BasicDialog
10496  * Dialog which provides adjustments for working with a layout in a Dialog.
10497  * Add your necessary layout config options to the dialog's config.<br>
10498  * Example usage (including a nested layout):
10499  * <pre><code>
10500 if(!dialog){
10501     dialog = new Roo.LayoutDialog("download-dlg", {
10502         modal: true,
10503         width:600,
10504         height:450,
10505         shadow:true,
10506         minWidth:500,
10507         minHeight:350,
10508         autoTabs:true,
10509         proxyDrag:true,
10510         // layout config merges with the dialog config
10511         center:{
10512             tabPosition: "top",
10513             alwaysShowTabs: true
10514         }
10515     });
10516     dialog.addKeyListener(27, dialog.hide, dialog);
10517     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10518     dialog.addButton("Build It!", this.getDownload, this);
10519
10520     // we can even add nested layouts
10521     var innerLayout = new Roo.BorderLayout("dl-inner", {
10522         east: {
10523             initialSize: 200,
10524             autoScroll:true,
10525             split:true
10526         },
10527         center: {
10528             autoScroll:true
10529         }
10530     });
10531     innerLayout.beginUpdate();
10532     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10533     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10534     innerLayout.endUpdate(true);
10535
10536     var layout = dialog.getLayout();
10537     layout.beginUpdate();
10538     layout.add("center", new Roo.ContentPanel("standard-panel",
10539                         {title: "Download the Source", fitToFrame:true}));
10540     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10541                {title: "Build your own roo.js"}));
10542     layout.getRegion("center").showPanel(sp);
10543     layout.endUpdate();
10544 }
10545 </code></pre>
10546     * @constructor
10547     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10548     * @param {Object} config configuration options
10549   */
10550 Roo.LayoutDialog = function(el, cfg){
10551     
10552     var config=  cfg;
10553     if (typeof(cfg) == 'undefined') {
10554         config = Roo.apply({}, el);
10555         // not sure why we use documentElement here.. - it should always be body.
10556         // IE7 borks horribly if we use documentElement.
10557         // webkit also does not like documentElement - it creates a body element...
10558         el = Roo.get( document.body || document.documentElement ).createChild();
10559         //config.autoCreate = true;
10560     }
10561     
10562     
10563     config.autoTabs = false;
10564     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10565     this.body.setStyle({overflow:"hidden", position:"relative"});
10566     this.layout = new Roo.BorderLayout(this.body.dom, config);
10567     this.layout.monitorWindowResize = false;
10568     this.el.addClass("x-dlg-auto-layout");
10569     // fix case when center region overwrites center function
10570     this.center = Roo.BasicDialog.prototype.center;
10571     this.on("show", this.layout.layout, this.layout, true);
10572     if (config.items) {
10573         var xitems = config.items;
10574         delete config.items;
10575         Roo.each(xitems, this.addxtype, this);
10576     }
10577     
10578     
10579 };
10580 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10581     /**
10582      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10583      * @deprecated
10584      */
10585     endUpdate : function(){
10586         this.layout.endUpdate();
10587     },
10588
10589     /**
10590      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10591      *  @deprecated
10592      */
10593     beginUpdate : function(){
10594         this.layout.beginUpdate();
10595     },
10596
10597     /**
10598      * Get the BorderLayout for this dialog
10599      * @return {Roo.BorderLayout}
10600      */
10601     getLayout : function(){
10602         return this.layout;
10603     },
10604
10605     showEl : function(){
10606         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10607         if(Roo.isIE7){
10608             this.layout.layout();
10609         }
10610     },
10611
10612     // private
10613     // Use the syncHeightBeforeShow config option to control this automatically
10614     syncBodyHeight : function(){
10615         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10616         if(this.layout){this.layout.layout();}
10617     },
10618     
10619       /**
10620      * Add an xtype element (actually adds to the layout.)
10621      * @return {Object} xdata xtype object data.
10622      */
10623     
10624     addxtype : function(c) {
10625         return this.layout.addxtype(c);
10626     }
10627 });/*
10628  * Based on:
10629  * Ext JS Library 1.1.1
10630  * Copyright(c) 2006-2007, Ext JS, LLC.
10631  *
10632  * Originally Released Under LGPL - original licence link has changed is not relivant.
10633  *
10634  * Fork - LGPL
10635  * <script type="text/javascript">
10636  */
10637  
10638 /**
10639  * @class Roo.MessageBox
10640  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10641  * Example usage:
10642  *<pre><code>
10643 // Basic alert:
10644 Roo.Msg.alert('Status', 'Changes saved successfully.');
10645
10646 // Prompt for user data:
10647 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10648     if (btn == 'ok'){
10649         // process text value...
10650     }
10651 });
10652
10653 // Show a dialog using config options:
10654 Roo.Msg.show({
10655    title:'Save Changes?',
10656    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10657    buttons: Roo.Msg.YESNOCANCEL,
10658    fn: processResult,
10659    animEl: 'elId'
10660 });
10661 </code></pre>
10662  * @singleton
10663  */
10664 Roo.MessageBox = function(){
10665     var dlg, opt, mask, waitTimer;
10666     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10667     var buttons, activeTextEl, bwidth;
10668
10669     // private
10670     var handleButton = function(button){
10671         dlg.hide();
10672         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10673     };
10674
10675     // private
10676     var handleHide = function(){
10677         if(opt && opt.cls){
10678             dlg.el.removeClass(opt.cls);
10679         }
10680         if(waitTimer){
10681             Roo.TaskMgr.stop(waitTimer);
10682             waitTimer = null;
10683         }
10684     };
10685
10686     // private
10687     var updateButtons = function(b){
10688         var width = 0;
10689         if(!b){
10690             buttons["ok"].hide();
10691             buttons["cancel"].hide();
10692             buttons["yes"].hide();
10693             buttons["no"].hide();
10694             dlg.footer.dom.style.display = 'none';
10695             return width;
10696         }
10697         dlg.footer.dom.style.display = '';
10698         for(var k in buttons){
10699             if(typeof buttons[k] != "function"){
10700                 if(b[k]){
10701                     buttons[k].show();
10702                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10703                     width += buttons[k].el.getWidth()+15;
10704                 }else{
10705                     buttons[k].hide();
10706                 }
10707             }
10708         }
10709         return width;
10710     };
10711
10712     // private
10713     var handleEsc = function(d, k, e){
10714         if(opt && opt.closable !== false){
10715             dlg.hide();
10716         }
10717         if(e){
10718             e.stopEvent();
10719         }
10720     };
10721
10722     return {
10723         /**
10724          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10725          * @return {Roo.BasicDialog} The BasicDialog element
10726          */
10727         getDialog : function(){
10728            if(!dlg){
10729                 dlg = new Roo.BasicDialog("x-msg-box", {
10730                     autoCreate : true,
10731                     shadow: true,
10732                     draggable: true,
10733                     resizable:false,
10734                     constraintoviewport:false,
10735                     fixedcenter:true,
10736                     collapsible : false,
10737                     shim:true,
10738                     modal: true,
10739                     width:400, height:100,
10740                     buttonAlign:"center",
10741                     closeClick : function(){
10742                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10743                             handleButton("no");
10744                         }else{
10745                             handleButton("cancel");
10746                         }
10747                     }
10748                 });
10749                 dlg.on("hide", handleHide);
10750                 mask = dlg.mask;
10751                 dlg.addKeyListener(27, handleEsc);
10752                 buttons = {};
10753                 var bt = this.buttonText;
10754                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10755                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10756                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10757                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10758                 bodyEl = dlg.body.createChild({
10759
10760                     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>'
10761                 });
10762                 msgEl = bodyEl.dom.firstChild;
10763                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10764                 textboxEl.enableDisplayMode();
10765                 textboxEl.addKeyListener([10,13], function(){
10766                     if(dlg.isVisible() && opt && opt.buttons){
10767                         if(opt.buttons.ok){
10768                             handleButton("ok");
10769                         }else if(opt.buttons.yes){
10770                             handleButton("yes");
10771                         }
10772                     }
10773                 });
10774                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10775                 textareaEl.enableDisplayMode();
10776                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10777                 progressEl.enableDisplayMode();
10778                 var pf = progressEl.dom.firstChild;
10779                 if (pf) {
10780                     pp = Roo.get(pf.firstChild);
10781                     pp.setHeight(pf.offsetHeight);
10782                 }
10783                 
10784             }
10785             return dlg;
10786         },
10787
10788         /**
10789          * Updates the message box body text
10790          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10791          * the XHTML-compliant non-breaking space character '&amp;#160;')
10792          * @return {Roo.MessageBox} This message box
10793          */
10794         updateText : function(text){
10795             if(!dlg.isVisible() && !opt.width){
10796                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10797             }
10798             msgEl.innerHTML = text || '&#160;';
10799       
10800             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10801             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10802             var w = Math.max(
10803                     Math.min(opt.width || cw , this.maxWidth), 
10804                     Math.max(opt.minWidth || this.minWidth, bwidth)
10805             );
10806             if(opt.prompt){
10807                 activeTextEl.setWidth(w);
10808             }
10809             if(dlg.isVisible()){
10810                 dlg.fixedcenter = false;
10811             }
10812             // to big, make it scroll. = But as usual stupid IE does not support
10813             // !important..
10814             
10815             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10816                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10817                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10818             } else {
10819                 bodyEl.dom.style.height = '';
10820                 bodyEl.dom.style.overflowY = '';
10821             }
10822             if (cw > w) {
10823                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10824             } else {
10825                 bodyEl.dom.style.overflowX = '';
10826             }
10827             
10828             dlg.setContentSize(w, bodyEl.getHeight());
10829             if(dlg.isVisible()){
10830                 dlg.fixedcenter = true;
10831             }
10832             return this;
10833         },
10834
10835         /**
10836          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10837          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10838          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10839          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10840          * @return {Roo.MessageBox} This message box
10841          */
10842         updateProgress : function(value, text){
10843             if(text){
10844                 this.updateText(text);
10845             }
10846             if (pp) { // weird bug on my firefox - for some reason this is not defined
10847                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10848             }
10849             return this;
10850         },        
10851
10852         /**
10853          * Returns true if the message box is currently displayed
10854          * @return {Boolean} True if the message box is visible, else false
10855          */
10856         isVisible : function(){
10857             return dlg && dlg.isVisible();  
10858         },
10859
10860         /**
10861          * Hides the message box if it is displayed
10862          */
10863         hide : function(){
10864             if(this.isVisible()){
10865                 dlg.hide();
10866             }  
10867         },
10868
10869         /**
10870          * Displays a new message box, or reinitializes an existing message box, based on the config options
10871          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10872          * The following config object properties are supported:
10873          * <pre>
10874 Property    Type             Description
10875 ----------  ---------------  ------------------------------------------------------------------------------------
10876 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10877                                    closes (defaults to undefined)
10878 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10879                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10880 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10881                                    progress and wait dialogs will ignore this property and always hide the
10882                                    close button as they can only be closed programmatically.
10883 cls               String           A custom CSS class to apply to the message box element
10884 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10885                                    displayed (defaults to 75)
10886 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10887                                    function will be btn (the name of the button that was clicked, if applicable,
10888                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10889                                    Progress and wait dialogs will ignore this option since they do not respond to
10890                                    user actions and can only be closed programmatically, so any required function
10891                                    should be called by the same code after it closes the dialog.
10892 icon              String           A CSS class that provides a background image to be used as an icon for
10893                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10894 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10895 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10896 modal             Boolean          False to allow user interaction with the page while the message box is
10897                                    displayed (defaults to true)
10898 msg               String           A string that will replace the existing message box body text (defaults
10899                                    to the XHTML-compliant non-breaking space character '&#160;')
10900 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10901 progress          Boolean          True to display a progress bar (defaults to false)
10902 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10903 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10904 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10905 title             String           The title text
10906 value             String           The string value to set into the active textbox element if displayed
10907 wait              Boolean          True to display a progress bar (defaults to false)
10908 width             Number           The width of the dialog in pixels
10909 </pre>
10910          *
10911          * Example usage:
10912          * <pre><code>
10913 Roo.Msg.show({
10914    title: 'Address',
10915    msg: 'Please enter your address:',
10916    width: 300,
10917    buttons: Roo.MessageBox.OKCANCEL,
10918    multiline: true,
10919    fn: saveAddress,
10920    animEl: 'addAddressBtn'
10921 });
10922 </code></pre>
10923          * @param {Object} config Configuration options
10924          * @return {Roo.MessageBox} This message box
10925          */
10926         show : function(options)
10927         {
10928             
10929             // this causes nightmares if you show one dialog after another
10930             // especially on callbacks..
10931              
10932             if(this.isVisible()){
10933                 
10934                 this.hide();
10935                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10936                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10937                 Roo.log("New Dialog Message:" +  options.msg )
10938                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10939                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10940                 
10941             }
10942             var d = this.getDialog();
10943             opt = options;
10944             d.setTitle(opt.title || "&#160;");
10945             d.close.setDisplayed(opt.closable !== false);
10946             activeTextEl = textboxEl;
10947             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10948             if(opt.prompt){
10949                 if(opt.multiline){
10950                     textboxEl.hide();
10951                     textareaEl.show();
10952                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10953                         opt.multiline : this.defaultTextHeight);
10954                     activeTextEl = textareaEl;
10955                 }else{
10956                     textboxEl.show();
10957                     textareaEl.hide();
10958                 }
10959             }else{
10960                 textboxEl.hide();
10961                 textareaEl.hide();
10962             }
10963             progressEl.setDisplayed(opt.progress === true);
10964             this.updateProgress(0);
10965             activeTextEl.dom.value = opt.value || "";
10966             if(opt.prompt){
10967                 dlg.setDefaultButton(activeTextEl);
10968             }else{
10969                 var bs = opt.buttons;
10970                 var db = null;
10971                 if(bs && bs.ok){
10972                     db = buttons["ok"];
10973                 }else if(bs && bs.yes){
10974                     db = buttons["yes"];
10975                 }
10976                 dlg.setDefaultButton(db);
10977             }
10978             bwidth = updateButtons(opt.buttons);
10979             this.updateText(opt.msg);
10980             if(opt.cls){
10981                 d.el.addClass(opt.cls);
10982             }
10983             d.proxyDrag = opt.proxyDrag === true;
10984             d.modal = opt.modal !== false;
10985             d.mask = opt.modal !== false ? mask : false;
10986             if(!d.isVisible()){
10987                 // force it to the end of the z-index stack so it gets a cursor in FF
10988                 document.body.appendChild(dlg.el.dom);
10989                 d.animateTarget = null;
10990                 d.show(options.animEl);
10991             }
10992             return this;
10993         },
10994
10995         /**
10996          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
10997          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
10998          * and closing the message box when the process is complete.
10999          * @param {String} title The title bar text
11000          * @param {String} msg The message box body text
11001          * @return {Roo.MessageBox} This message box
11002          */
11003         progress : function(title, msg){
11004             this.show({
11005                 title : title,
11006                 msg : msg,
11007                 buttons: false,
11008                 progress:true,
11009                 closable:false,
11010                 minWidth: this.minProgressWidth,
11011                 modal : true
11012             });
11013             return this;
11014         },
11015
11016         /**
11017          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
11018          * If a callback function is passed it will be called after the user clicks the button, and the
11019          * id of the button that was clicked will be passed as the only parameter to the callback
11020          * (could also be the top-right close button).
11021          * @param {String} title The title bar text
11022          * @param {String} msg The message box body text
11023          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11024          * @param {Object} scope (optional) The scope of the callback function
11025          * @return {Roo.MessageBox} This message box
11026          */
11027         alert : function(title, msg, fn, scope){
11028             this.show({
11029                 title : title,
11030                 msg : msg,
11031                 buttons: this.OK,
11032                 fn: fn,
11033                 scope : scope,
11034                 modal : true
11035             });
11036             return this;
11037         },
11038
11039         /**
11040          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
11041          * interaction while waiting for a long-running process to complete that does not have defined intervals.
11042          * You are responsible for closing the message box when the process is complete.
11043          * @param {String} msg The message box body text
11044          * @param {String} title (optional) The title bar text
11045          * @return {Roo.MessageBox} This message box
11046          */
11047         wait : function(msg, title){
11048             this.show({
11049                 title : title,
11050                 msg : msg,
11051                 buttons: false,
11052                 closable:false,
11053                 progress:true,
11054                 modal:true,
11055                 width:300,
11056                 wait:true
11057             });
11058             waitTimer = Roo.TaskMgr.start({
11059                 run: function(i){
11060                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
11061                 },
11062                 interval: 1000
11063             });
11064             return this;
11065         },
11066
11067         /**
11068          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
11069          * If a callback function is passed it will be called after the user clicks either button, and the id of the
11070          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
11071          * @param {String} title The title bar text
11072          * @param {String} msg The message box body text
11073          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11074          * @param {Object} scope (optional) The scope of the callback function
11075          * @return {Roo.MessageBox} This message box
11076          */
11077         confirm : function(title, msg, fn, scope){
11078             this.show({
11079                 title : title,
11080                 msg : msg,
11081                 buttons: this.YESNO,
11082                 fn: fn,
11083                 scope : scope,
11084                 modal : true
11085             });
11086             return this;
11087         },
11088
11089         /**
11090          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
11091          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
11092          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
11093          * (could also be the top-right close button) and the text that was entered will be passed as the two
11094          * parameters to the callback.
11095          * @param {String} title The title bar text
11096          * @param {String} msg The message box body text
11097          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11098          * @param {Object} scope (optional) The scope of the callback function
11099          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
11100          * property, or the height in pixels to create the textbox (defaults to false / single-line)
11101          * @return {Roo.MessageBox} This message box
11102          */
11103         prompt : function(title, msg, fn, scope, multiline){
11104             this.show({
11105                 title : title,
11106                 msg : msg,
11107                 buttons: this.OKCANCEL,
11108                 fn: fn,
11109                 minWidth:250,
11110                 scope : scope,
11111                 prompt:true,
11112                 multiline: multiline,
11113                 modal : true
11114             });
11115             return this;
11116         },
11117
11118         /**
11119          * Button config that displays a single OK button
11120          * @type Object
11121          */
11122         OK : {ok:true},
11123         /**
11124          * Button config that displays Yes and No buttons
11125          * @type Object
11126          */
11127         YESNO : {yes:true, no:true},
11128         /**
11129          * Button config that displays OK and Cancel buttons
11130          * @type Object
11131          */
11132         OKCANCEL : {ok:true, cancel:true},
11133         /**
11134          * Button config that displays Yes, No and Cancel buttons
11135          * @type Object
11136          */
11137         YESNOCANCEL : {yes:true, no:true, cancel:true},
11138
11139         /**
11140          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
11141          * @type Number
11142          */
11143         defaultTextHeight : 75,
11144         /**
11145          * The maximum width in pixels of the message box (defaults to 600)
11146          * @type Number
11147          */
11148         maxWidth : 600,
11149         /**
11150          * The minimum width in pixels of the message box (defaults to 100)
11151          * @type Number
11152          */
11153         minWidth : 100,
11154         /**
11155          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
11156          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
11157          * @type Number
11158          */
11159         minProgressWidth : 250,
11160         /**
11161          * An object containing the default button text strings that can be overriden for localized language support.
11162          * Supported properties are: ok, cancel, yes and no.
11163          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
11164          * @type Object
11165          */
11166         buttonText : {
11167             ok : "OK",
11168             cancel : "Cancel",
11169             yes : "Yes",
11170             no : "No"
11171         }
11172     };
11173 }();
11174
11175 /**
11176  * Shorthand for {@link Roo.MessageBox}
11177  */
11178 Roo.Msg = Roo.MessageBox;/*
11179  * Based on:
11180  * Ext JS Library 1.1.1
11181  * Copyright(c) 2006-2007, Ext JS, LLC.
11182  *
11183  * Originally Released Under LGPL - original licence link has changed is not relivant.
11184  *
11185  * Fork - LGPL
11186  * <script type="text/javascript">
11187  */
11188 /**
11189  * @class Roo.QuickTips
11190  * Provides attractive and customizable tooltips for any element.
11191  * @singleton
11192  */
11193 Roo.QuickTips = function(){
11194     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
11195     var ce, bd, xy, dd;
11196     var visible = false, disabled = true, inited = false;
11197     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
11198     
11199     var onOver = function(e){
11200         if(disabled){
11201             return;
11202         }
11203         var t = e.getTarget();
11204         if(!t || t.nodeType !== 1 || t == document || t == document.body){
11205             return;
11206         }
11207         if(ce && t == ce.el){
11208             clearTimeout(hideProc);
11209             return;
11210         }
11211         if(t && tagEls[t.id]){
11212             tagEls[t.id].el = t;
11213             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
11214             return;
11215         }
11216         var ttp, et = Roo.fly(t);
11217         var ns = cfg.namespace;
11218         if(tm.interceptTitles && t.title){
11219             ttp = t.title;
11220             t.qtip = ttp;
11221             t.removeAttribute("title");
11222             e.preventDefault();
11223         }else{
11224             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
11225         }
11226         if(ttp){
11227             showProc = show.defer(tm.showDelay, tm, [{
11228                 el: t, 
11229                 text: ttp.replace(/\\n/g,'<br/>'),
11230                 width: et.getAttributeNS(ns, cfg.width),
11231                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
11232                 title: et.getAttributeNS(ns, cfg.title),
11233                     cls: et.getAttributeNS(ns, cfg.cls)
11234             }]);
11235         }
11236     };
11237     
11238     var onOut = function(e){
11239         clearTimeout(showProc);
11240         var t = e.getTarget();
11241         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
11242             hideProc = setTimeout(hide, tm.hideDelay);
11243         }
11244     };
11245     
11246     var onMove = function(e){
11247         if(disabled){
11248             return;
11249         }
11250         xy = e.getXY();
11251         xy[1] += 18;
11252         if(tm.trackMouse && ce){
11253             el.setXY(xy);
11254         }
11255     };
11256     
11257     var onDown = function(e){
11258         clearTimeout(showProc);
11259         clearTimeout(hideProc);
11260         if(!e.within(el)){
11261             if(tm.hideOnClick){
11262                 hide();
11263                 tm.disable();
11264                 tm.enable.defer(100, tm);
11265             }
11266         }
11267     };
11268     
11269     var getPad = function(){
11270         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
11271     };
11272
11273     var show = function(o){
11274         if(disabled){
11275             return;
11276         }
11277         clearTimeout(dismissProc);
11278         ce = o;
11279         if(removeCls){ // in case manually hidden
11280             el.removeClass(removeCls);
11281             removeCls = null;
11282         }
11283         if(ce.cls){
11284             el.addClass(ce.cls);
11285             removeCls = ce.cls;
11286         }
11287         if(ce.title){
11288             tipTitle.update(ce.title);
11289             tipTitle.show();
11290         }else{
11291             tipTitle.update('');
11292             tipTitle.hide();
11293         }
11294         el.dom.style.width  = tm.maxWidth+'px';
11295         //tipBody.dom.style.width = '';
11296         tipBodyText.update(o.text);
11297         var p = getPad(), w = ce.width;
11298         if(!w){
11299             var td = tipBodyText.dom;
11300             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
11301             if(aw > tm.maxWidth){
11302                 w = tm.maxWidth;
11303             }else if(aw < tm.minWidth){
11304                 w = tm.minWidth;
11305             }else{
11306                 w = aw;
11307             }
11308         }
11309         //tipBody.setWidth(w);
11310         el.setWidth(parseInt(w, 10) + p);
11311         if(ce.autoHide === false){
11312             close.setDisplayed(true);
11313             if(dd){
11314                 dd.unlock();
11315             }
11316         }else{
11317             close.setDisplayed(false);
11318             if(dd){
11319                 dd.lock();
11320             }
11321         }
11322         if(xy){
11323             el.avoidY = xy[1]-18;
11324             el.setXY(xy);
11325         }
11326         if(tm.animate){
11327             el.setOpacity(.1);
11328             el.setStyle("visibility", "visible");
11329             el.fadeIn({callback: afterShow});
11330         }else{
11331             afterShow();
11332         }
11333     };
11334     
11335     var afterShow = function(){
11336         if(ce){
11337             el.show();
11338             esc.enable();
11339             if(tm.autoDismiss && ce.autoHide !== false){
11340                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
11341             }
11342         }
11343     };
11344     
11345     var hide = function(noanim){
11346         clearTimeout(dismissProc);
11347         clearTimeout(hideProc);
11348         ce = null;
11349         if(el.isVisible()){
11350             esc.disable();
11351             if(noanim !== true && tm.animate){
11352                 el.fadeOut({callback: afterHide});
11353             }else{
11354                 afterHide();
11355             } 
11356         }
11357     };
11358     
11359     var afterHide = function(){
11360         el.hide();
11361         if(removeCls){
11362             el.removeClass(removeCls);
11363             removeCls = null;
11364         }
11365     };
11366     
11367     return {
11368         /**
11369         * @cfg {Number} minWidth
11370         * The minimum width of the quick tip (defaults to 40)
11371         */
11372        minWidth : 40,
11373         /**
11374         * @cfg {Number} maxWidth
11375         * The maximum width of the quick tip (defaults to 300)
11376         */
11377        maxWidth : 300,
11378         /**
11379         * @cfg {Boolean} interceptTitles
11380         * True to automatically use the element's DOM title value if available (defaults to false)
11381         */
11382        interceptTitles : false,
11383         /**
11384         * @cfg {Boolean} trackMouse
11385         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11386         */
11387        trackMouse : false,
11388         /**
11389         * @cfg {Boolean} hideOnClick
11390         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11391         */
11392        hideOnClick : true,
11393         /**
11394         * @cfg {Number} showDelay
11395         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11396         */
11397        showDelay : 500,
11398         /**
11399         * @cfg {Number} hideDelay
11400         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11401         */
11402        hideDelay : 200,
11403         /**
11404         * @cfg {Boolean} autoHide
11405         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11406         * Used in conjunction with hideDelay.
11407         */
11408        autoHide : true,
11409         /**
11410         * @cfg {Boolean}
11411         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11412         * (defaults to true).  Used in conjunction with autoDismissDelay.
11413         */
11414        autoDismiss : true,
11415         /**
11416         * @cfg {Number}
11417         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11418         */
11419        autoDismissDelay : 5000,
11420        /**
11421         * @cfg {Boolean} animate
11422         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11423         */
11424        animate : false,
11425
11426        /**
11427         * @cfg {String} title
11428         * Title text to display (defaults to '').  This can be any valid HTML markup.
11429         */
11430         title: '',
11431        /**
11432         * @cfg {String} text
11433         * Body text to display (defaults to '').  This can be any valid HTML markup.
11434         */
11435         text : '',
11436        /**
11437         * @cfg {String} cls
11438         * A CSS class to apply to the base quick tip element (defaults to '').
11439         */
11440         cls : '',
11441        /**
11442         * @cfg {Number} width
11443         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11444         * minWidth or maxWidth.
11445         */
11446         width : null,
11447
11448     /**
11449      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11450      * or display QuickTips in a page.
11451      */
11452        init : function(){
11453           tm = Roo.QuickTips;
11454           cfg = tm.tagConfig;
11455           if(!inited){
11456               if(!Roo.isReady){ // allow calling of init() before onReady
11457                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11458                   return;
11459               }
11460               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11461               el.fxDefaults = {stopFx: true};
11462               // maximum custom styling
11463               //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>');
11464               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>');              
11465               tipTitle = el.child('h3');
11466               tipTitle.enableDisplayMode("block");
11467               tipBody = el.child('div.x-tip-bd');
11468               tipBodyText = el.child('div.x-tip-bd-inner');
11469               //bdLeft = el.child('div.x-tip-bd-left');
11470               //bdRight = el.child('div.x-tip-bd-right');
11471               close = el.child('div.x-tip-close');
11472               close.enableDisplayMode("block");
11473               close.on("click", hide);
11474               var d = Roo.get(document);
11475               d.on("mousedown", onDown);
11476               d.on("mouseover", onOver);
11477               d.on("mouseout", onOut);
11478               d.on("mousemove", onMove);
11479               esc = d.addKeyListener(27, hide);
11480               esc.disable();
11481               if(Roo.dd.DD){
11482                   dd = el.initDD("default", null, {
11483                       onDrag : function(){
11484                           el.sync();  
11485                       }
11486                   });
11487                   dd.setHandleElId(tipTitle.id);
11488                   dd.lock();
11489               }
11490               inited = true;
11491           }
11492           this.enable(); 
11493        },
11494
11495     /**
11496      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11497      * are supported:
11498      * <pre>
11499 Property    Type                   Description
11500 ----------  ---------------------  ------------------------------------------------------------------------
11501 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11502      * </ul>
11503      * @param {Object} config The config object
11504      */
11505        register : function(config){
11506            var cs = config instanceof Array ? config : arguments;
11507            for(var i = 0, len = cs.length; i < len; i++) {
11508                var c = cs[i];
11509                var target = c.target;
11510                if(target){
11511                    if(target instanceof Array){
11512                        for(var j = 0, jlen = target.length; j < jlen; j++){
11513                            tagEls[target[j]] = c;
11514                        }
11515                    }else{
11516                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11517                    }
11518                }
11519            }
11520        },
11521
11522     /**
11523      * Removes this quick tip from its element and destroys it.
11524      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11525      */
11526        unregister : function(el){
11527            delete tagEls[Roo.id(el)];
11528        },
11529
11530     /**
11531      * Enable this quick tip.
11532      */
11533        enable : function(){
11534            if(inited && disabled){
11535                locks.pop();
11536                if(locks.length < 1){
11537                    disabled = false;
11538                }
11539            }
11540        },
11541
11542     /**
11543      * Disable this quick tip.
11544      */
11545        disable : function(){
11546           disabled = true;
11547           clearTimeout(showProc);
11548           clearTimeout(hideProc);
11549           clearTimeout(dismissProc);
11550           if(ce){
11551               hide(true);
11552           }
11553           locks.push(1);
11554        },
11555
11556     /**
11557      * Returns true if the quick tip is enabled, else false.
11558      */
11559        isEnabled : function(){
11560             return !disabled;
11561        },
11562
11563         // private
11564        tagConfig : {
11565            namespace : "roo", // was ext?? this may break..
11566            alt_namespace : "ext",
11567            attribute : "qtip",
11568            width : "width",
11569            target : "target",
11570            title : "qtitle",
11571            hide : "hide",
11572            cls : "qclass"
11573        }
11574    };
11575 }();
11576
11577 // backwards compat
11578 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11579  * Based on:
11580  * Ext JS Library 1.1.1
11581  * Copyright(c) 2006-2007, Ext JS, LLC.
11582  *
11583  * Originally Released Under LGPL - original licence link has changed is not relivant.
11584  *
11585  * Fork - LGPL
11586  * <script type="text/javascript">
11587  */
11588  
11589
11590 /**
11591  * @class Roo.tree.TreePanel
11592  * @extends Roo.data.Tree
11593
11594  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11595  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11596  * @cfg {Boolean} enableDD true to enable drag and drop
11597  * @cfg {Boolean} enableDrag true to enable just drag
11598  * @cfg {Boolean} enableDrop true to enable just drop
11599  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11600  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11601  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11602  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11603  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11604  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11605  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11606  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11607  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11608  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11609  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11610  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
11611  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
11612  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11613  * @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>
11614  * @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>
11615  * 
11616  * @constructor
11617  * @param {String/HTMLElement/Element} el The container element
11618  * @param {Object} config
11619  */
11620 Roo.tree.TreePanel = function(el, config){
11621     var root = false;
11622     var loader = false;
11623     if (config.root) {
11624         root = config.root;
11625         delete config.root;
11626     }
11627     if (config.loader) {
11628         loader = config.loader;
11629         delete config.loader;
11630     }
11631     
11632     Roo.apply(this, config);
11633     Roo.tree.TreePanel.superclass.constructor.call(this);
11634     this.el = Roo.get(el);
11635     this.el.addClass('x-tree');
11636     //console.log(root);
11637     if (root) {
11638         this.setRootNode( Roo.factory(root, Roo.tree));
11639     }
11640     if (loader) {
11641         this.loader = Roo.factory(loader, Roo.tree);
11642     }
11643    /**
11644     * Read-only. The id of the container element becomes this TreePanel's id.
11645     */
11646     this.id = this.el.id;
11647     this.addEvents({
11648         /**
11649         * @event beforeload
11650         * Fires before a node is loaded, return false to cancel
11651         * @param {Node} node The node being loaded
11652         */
11653         "beforeload" : true,
11654         /**
11655         * @event load
11656         * Fires when a node is loaded
11657         * @param {Node} node The node that was loaded
11658         */
11659         "load" : true,
11660         /**
11661         * @event textchange
11662         * Fires when the text for a node is changed
11663         * @param {Node} node The node
11664         * @param {String} text The new text
11665         * @param {String} oldText The old text
11666         */
11667         "textchange" : true,
11668         /**
11669         * @event beforeexpand
11670         * Fires before a node is expanded, return false to cancel.
11671         * @param {Node} node The node
11672         * @param {Boolean} deep
11673         * @param {Boolean} anim
11674         */
11675         "beforeexpand" : true,
11676         /**
11677         * @event beforecollapse
11678         * Fires before a node is collapsed, return false to cancel.
11679         * @param {Node} node The node
11680         * @param {Boolean} deep
11681         * @param {Boolean} anim
11682         */
11683         "beforecollapse" : true,
11684         /**
11685         * @event expand
11686         * Fires when a node is expanded
11687         * @param {Node} node The node
11688         */
11689         "expand" : true,
11690         /**
11691         * @event disabledchange
11692         * Fires when the disabled status of a node changes
11693         * @param {Node} node The node
11694         * @param {Boolean} disabled
11695         */
11696         "disabledchange" : true,
11697         /**
11698         * @event collapse
11699         * Fires when a node is collapsed
11700         * @param {Node} node The node
11701         */
11702         "collapse" : true,
11703         /**
11704         * @event beforeclick
11705         * Fires before click processing on a node. Return false to cancel the default action.
11706         * @param {Node} node The node
11707         * @param {Roo.EventObject} e The event object
11708         */
11709         "beforeclick":true,
11710         /**
11711         * @event checkchange
11712         * Fires when a node with a checkbox's checked property changes
11713         * @param {Node} this This node
11714         * @param {Boolean} checked
11715         */
11716         "checkchange":true,
11717         /**
11718         * @event click
11719         * Fires when a node is clicked
11720         * @param {Node} node The node
11721         * @param {Roo.EventObject} e The event object
11722         */
11723         "click":true,
11724         /**
11725         * @event dblclick
11726         * Fires when a node is double clicked
11727         * @param {Node} node The node
11728         * @param {Roo.EventObject} e The event object
11729         */
11730         "dblclick":true,
11731         /**
11732         * @event contextmenu
11733         * Fires when a node is right clicked
11734         * @param {Node} node The node
11735         * @param {Roo.EventObject} e The event object
11736         */
11737         "contextmenu":true,
11738         /**
11739         * @event beforechildrenrendered
11740         * Fires right before the child nodes for a node are rendered
11741         * @param {Node} node The node
11742         */
11743         "beforechildrenrendered":true,
11744         /**
11745         * @event startdrag
11746         * Fires when a node starts being dragged
11747         * @param {Roo.tree.TreePanel} this
11748         * @param {Roo.tree.TreeNode} node
11749         * @param {event} e The raw browser event
11750         */ 
11751        "startdrag" : true,
11752        /**
11753         * @event enddrag
11754         * Fires when a drag operation is complete
11755         * @param {Roo.tree.TreePanel} this
11756         * @param {Roo.tree.TreeNode} node
11757         * @param {event} e The raw browser event
11758         */
11759        "enddrag" : true,
11760        /**
11761         * @event dragdrop
11762         * Fires when a dragged node is dropped on a valid DD target
11763         * @param {Roo.tree.TreePanel} this
11764         * @param {Roo.tree.TreeNode} node
11765         * @param {DD} dd The dd it was dropped on
11766         * @param {event} e The raw browser event
11767         */
11768        "dragdrop" : true,
11769        /**
11770         * @event beforenodedrop
11771         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11772         * passed to handlers has the following properties:<br />
11773         * <ul style="padding:5px;padding-left:16px;">
11774         * <li>tree - The TreePanel</li>
11775         * <li>target - The node being targeted for the drop</li>
11776         * <li>data - The drag data from the drag source</li>
11777         * <li>point - The point of the drop - append, above or below</li>
11778         * <li>source - The drag source</li>
11779         * <li>rawEvent - Raw mouse event</li>
11780         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11781         * to be inserted by setting them on this object.</li>
11782         * <li>cancel - Set this to true to cancel the drop.</li>
11783         * </ul>
11784         * @param {Object} dropEvent
11785         */
11786        "beforenodedrop" : true,
11787        /**
11788         * @event nodedrop
11789         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11790         * passed to handlers has the following properties:<br />
11791         * <ul style="padding:5px;padding-left:16px;">
11792         * <li>tree - The TreePanel</li>
11793         * <li>target - The node being targeted for the drop</li>
11794         * <li>data - The drag data from the drag source</li>
11795         * <li>point - The point of the drop - append, above or below</li>
11796         * <li>source - The drag source</li>
11797         * <li>rawEvent - Raw mouse event</li>
11798         * <li>dropNode - Dropped node(s).</li>
11799         * </ul>
11800         * @param {Object} dropEvent
11801         */
11802        "nodedrop" : true,
11803         /**
11804         * @event nodedragover
11805         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11806         * passed to handlers has the following properties:<br />
11807         * <ul style="padding:5px;padding-left:16px;">
11808         * <li>tree - The TreePanel</li>
11809         * <li>target - The node being targeted for the drop</li>
11810         * <li>data - The drag data from the drag source</li>
11811         * <li>point - The point of the drop - append, above or below</li>
11812         * <li>source - The drag source</li>
11813         * <li>rawEvent - Raw mouse event</li>
11814         * <li>dropNode - Drop node(s) provided by the source.</li>
11815         * <li>cancel - Set this to true to signal drop not allowed.</li>
11816         * </ul>
11817         * @param {Object} dragOverEvent
11818         */
11819        "nodedragover" : true
11820         
11821     });
11822     if(this.singleExpand){
11823        this.on("beforeexpand", this.restrictExpand, this);
11824     }
11825     if (this.editor) {
11826         this.editor.tree = this;
11827         this.editor = Roo.factory(this.editor, Roo.tree);
11828     }
11829     
11830     if (this.selModel) {
11831         this.selModel = Roo.factory(this.selModel, Roo.tree);
11832     }
11833    
11834 };
11835 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11836     rootVisible : true,
11837     animate: Roo.enableFx,
11838     lines : true,
11839     enableDD : false,
11840     hlDrop : Roo.enableFx,
11841   
11842     renderer: false,
11843     
11844     rendererTip: false,
11845     // private
11846     restrictExpand : function(node){
11847         var p = node.parentNode;
11848         if(p){
11849             if(p.expandedChild && p.expandedChild.parentNode == p){
11850                 p.expandedChild.collapse();
11851             }
11852             p.expandedChild = node;
11853         }
11854     },
11855
11856     // private override
11857     setRootNode : function(node){
11858         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11859         if(!this.rootVisible){
11860             node.ui = new Roo.tree.RootTreeNodeUI(node);
11861         }
11862         return node;
11863     },
11864
11865     /**
11866      * Returns the container element for this TreePanel
11867      */
11868     getEl : function(){
11869         return this.el;
11870     },
11871
11872     /**
11873      * Returns the default TreeLoader for this TreePanel
11874      */
11875     getLoader : function(){
11876         return this.loader;
11877     },
11878
11879     /**
11880      * Expand all nodes
11881      */
11882     expandAll : function(){
11883         this.root.expand(true);
11884     },
11885
11886     /**
11887      * Collapse all nodes
11888      */
11889     collapseAll : function(){
11890         this.root.collapse(true);
11891     },
11892
11893     /**
11894      * Returns the selection model used by this TreePanel
11895      */
11896     getSelectionModel : function(){
11897         if(!this.selModel){
11898             this.selModel = new Roo.tree.DefaultSelectionModel();
11899         }
11900         return this.selModel;
11901     },
11902
11903     /**
11904      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11905      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11906      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11907      * @return {Array}
11908      */
11909     getChecked : function(a, startNode){
11910         startNode = startNode || this.root;
11911         var r = [];
11912         var f = function(){
11913             if(this.attributes.checked){
11914                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11915             }
11916         }
11917         startNode.cascade(f);
11918         return r;
11919     },
11920
11921     /**
11922      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11923      * @param {String} path
11924      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11925      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11926      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11927      */
11928     expandPath : function(path, attr, callback){
11929         attr = attr || "id";
11930         var keys = path.split(this.pathSeparator);
11931         var curNode = this.root;
11932         if(curNode.attributes[attr] != keys[1]){ // invalid root
11933             if(callback){
11934                 callback(false, null);
11935             }
11936             return;
11937         }
11938         var index = 1;
11939         var f = function(){
11940             if(++index == keys.length){
11941                 if(callback){
11942                     callback(true, curNode);
11943                 }
11944                 return;
11945             }
11946             var c = curNode.findChild(attr, keys[index]);
11947             if(!c){
11948                 if(callback){
11949                     callback(false, curNode);
11950                 }
11951                 return;
11952             }
11953             curNode = c;
11954             c.expand(false, false, f);
11955         };
11956         curNode.expand(false, false, f);
11957     },
11958
11959     /**
11960      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11961      * @param {String} path
11962      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11963      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11964      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11965      */
11966     selectPath : function(path, attr, callback){
11967         attr = attr || "id";
11968         var keys = path.split(this.pathSeparator);
11969         var v = keys.pop();
11970         if(keys.length > 0){
11971             var f = function(success, node){
11972                 if(success && node){
11973                     var n = node.findChild(attr, v);
11974                     if(n){
11975                         n.select();
11976                         if(callback){
11977                             callback(true, n);
11978                         }
11979                     }else if(callback){
11980                         callback(false, n);
11981                     }
11982                 }else{
11983                     if(callback){
11984                         callback(false, n);
11985                     }
11986                 }
11987             };
11988             this.expandPath(keys.join(this.pathSeparator), attr, f);
11989         }else{
11990             this.root.select();
11991             if(callback){
11992                 callback(true, this.root);
11993             }
11994         }
11995     },
11996
11997     getTreeEl : function(){
11998         return this.el;
11999     },
12000
12001     /**
12002      * Trigger rendering of this TreePanel
12003      */
12004     render : function(){
12005         if (this.innerCt) {
12006             return this; // stop it rendering more than once!!
12007         }
12008         
12009         this.innerCt = this.el.createChild({tag:"ul",
12010                cls:"x-tree-root-ct " +
12011                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
12012
12013         if(this.containerScroll){
12014             Roo.dd.ScrollManager.register(this.el);
12015         }
12016         if((this.enableDD || this.enableDrop) && !this.dropZone){
12017            /**
12018             * The dropZone used by this tree if drop is enabled
12019             * @type Roo.tree.TreeDropZone
12020             */
12021              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
12022                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
12023            });
12024         }
12025         if((this.enableDD || this.enableDrag) && !this.dragZone){
12026            /**
12027             * The dragZone used by this tree if drag is enabled
12028             * @type Roo.tree.TreeDragZone
12029             */
12030             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
12031                ddGroup: this.ddGroup || "TreeDD",
12032                scroll: this.ddScroll
12033            });
12034         }
12035         this.getSelectionModel().init(this);
12036         if (!this.root) {
12037             Roo.log("ROOT not set in tree");
12038             return this;
12039         }
12040         this.root.render();
12041         if(!this.rootVisible){
12042             this.root.renderChildren();
12043         }
12044         return this;
12045     }
12046 });/*
12047  * Based on:
12048  * Ext JS Library 1.1.1
12049  * Copyright(c) 2006-2007, Ext JS, LLC.
12050  *
12051  * Originally Released Under LGPL - original licence link has changed is not relivant.
12052  *
12053  * Fork - LGPL
12054  * <script type="text/javascript">
12055  */
12056  
12057
12058 /**
12059  * @class Roo.tree.DefaultSelectionModel
12060  * @extends Roo.util.Observable
12061  * The default single selection for a TreePanel.
12062  * @param {Object} cfg Configuration
12063  */
12064 Roo.tree.DefaultSelectionModel = function(cfg){
12065    this.selNode = null;
12066    
12067    
12068    
12069    this.addEvents({
12070        /**
12071         * @event selectionchange
12072         * Fires when the selected node changes
12073         * @param {DefaultSelectionModel} this
12074         * @param {TreeNode} node the new selection
12075         */
12076        "selectionchange" : true,
12077
12078        /**
12079         * @event beforeselect
12080         * Fires before the selected node changes, return false to cancel the change
12081         * @param {DefaultSelectionModel} this
12082         * @param {TreeNode} node the new selection
12083         * @param {TreeNode} node the old selection
12084         */
12085        "beforeselect" : true
12086    });
12087    
12088     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
12089 };
12090
12091 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
12092     init : function(tree){
12093         this.tree = tree;
12094         tree.getTreeEl().on("keydown", this.onKeyDown, this);
12095         tree.on("click", this.onNodeClick, this);
12096     },
12097     
12098     onNodeClick : function(node, e){
12099         if (e.ctrlKey && this.selNode == node)  {
12100             this.unselect(node);
12101             return;
12102         }
12103         this.select(node);
12104     },
12105     
12106     /**
12107      * Select a node.
12108      * @param {TreeNode} node The node to select
12109      * @return {TreeNode} The selected node
12110      */
12111     select : function(node){
12112         var last = this.selNode;
12113         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
12114             if(last){
12115                 last.ui.onSelectedChange(false);
12116             }
12117             this.selNode = node;
12118             node.ui.onSelectedChange(true);
12119             this.fireEvent("selectionchange", this, node, last);
12120         }
12121         return node;
12122     },
12123     
12124     /**
12125      * Deselect a node.
12126      * @param {TreeNode} node The node to unselect
12127      */
12128     unselect : function(node){
12129         if(this.selNode == node){
12130             this.clearSelections();
12131         }    
12132     },
12133     
12134     /**
12135      * Clear all selections
12136      */
12137     clearSelections : function(){
12138         var n = this.selNode;
12139         if(n){
12140             n.ui.onSelectedChange(false);
12141             this.selNode = null;
12142             this.fireEvent("selectionchange", this, null);
12143         }
12144         return n;
12145     },
12146     
12147     /**
12148      * Get the selected node
12149      * @return {TreeNode} The selected node
12150      */
12151     getSelectedNode : function(){
12152         return this.selNode;    
12153     },
12154     
12155     /**
12156      * Returns true if the node is selected
12157      * @param {TreeNode} node The node to check
12158      * @return {Boolean}
12159      */
12160     isSelected : function(node){
12161         return this.selNode == node;  
12162     },
12163
12164     /**
12165      * Selects the node above the selected node in the tree, intelligently walking the nodes
12166      * @return TreeNode The new selection
12167      */
12168     selectPrevious : function(){
12169         var s = this.selNode || this.lastSelNode;
12170         if(!s){
12171             return null;
12172         }
12173         var ps = s.previousSibling;
12174         if(ps){
12175             if(!ps.isExpanded() || ps.childNodes.length < 1){
12176                 return this.select(ps);
12177             } else{
12178                 var lc = ps.lastChild;
12179                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
12180                     lc = lc.lastChild;
12181                 }
12182                 return this.select(lc);
12183             }
12184         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
12185             return this.select(s.parentNode);
12186         }
12187         return null;
12188     },
12189
12190     /**
12191      * Selects the node above the selected node in the tree, intelligently walking the nodes
12192      * @return TreeNode The new selection
12193      */
12194     selectNext : function(){
12195         var s = this.selNode || this.lastSelNode;
12196         if(!s){
12197             return null;
12198         }
12199         if(s.firstChild && s.isExpanded()){
12200              return this.select(s.firstChild);
12201          }else if(s.nextSibling){
12202              return this.select(s.nextSibling);
12203          }else if(s.parentNode){
12204             var newS = null;
12205             s.parentNode.bubble(function(){
12206                 if(this.nextSibling){
12207                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
12208                     return false;
12209                 }
12210             });
12211             return newS;
12212          }
12213         return null;
12214     },
12215
12216     onKeyDown : function(e){
12217         var s = this.selNode || this.lastSelNode;
12218         // undesirable, but required
12219         var sm = this;
12220         if(!s){
12221             return;
12222         }
12223         var k = e.getKey();
12224         switch(k){
12225              case e.DOWN:
12226                  e.stopEvent();
12227                  this.selectNext();
12228              break;
12229              case e.UP:
12230                  e.stopEvent();
12231                  this.selectPrevious();
12232              break;
12233              case e.RIGHT:
12234                  e.preventDefault();
12235                  if(s.hasChildNodes()){
12236                      if(!s.isExpanded()){
12237                          s.expand();
12238                      }else if(s.firstChild){
12239                          this.select(s.firstChild, e);
12240                      }
12241                  }
12242              break;
12243              case e.LEFT:
12244                  e.preventDefault();
12245                  if(s.hasChildNodes() && s.isExpanded()){
12246                      s.collapse();
12247                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
12248                      this.select(s.parentNode, e);
12249                  }
12250              break;
12251         };
12252     }
12253 });
12254
12255 /**
12256  * @class Roo.tree.MultiSelectionModel
12257  * @extends Roo.util.Observable
12258  * Multi selection for a TreePanel.
12259  * @param {Object} cfg Configuration
12260  */
12261 Roo.tree.MultiSelectionModel = function(){
12262    this.selNodes = [];
12263    this.selMap = {};
12264    this.addEvents({
12265        /**
12266         * @event selectionchange
12267         * Fires when the selected nodes change
12268         * @param {MultiSelectionModel} this
12269         * @param {Array} nodes Array of the selected nodes
12270         */
12271        "selectionchange" : true
12272    });
12273    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
12274    
12275 };
12276
12277 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
12278     init : function(tree){
12279         this.tree = tree;
12280         tree.getTreeEl().on("keydown", this.onKeyDown, this);
12281         tree.on("click", this.onNodeClick, this);
12282     },
12283     
12284     onNodeClick : function(node, e){
12285         this.select(node, e, e.ctrlKey);
12286     },
12287     
12288     /**
12289      * Select a node.
12290      * @param {TreeNode} node The node to select
12291      * @param {EventObject} e (optional) An event associated with the selection
12292      * @param {Boolean} keepExisting True to retain existing selections
12293      * @return {TreeNode} The selected node
12294      */
12295     select : function(node, e, keepExisting){
12296         if(keepExisting !== true){
12297             this.clearSelections(true);
12298         }
12299         if(this.isSelected(node)){
12300             this.lastSelNode = node;
12301             return node;
12302         }
12303         this.selNodes.push(node);
12304         this.selMap[node.id] = node;
12305         this.lastSelNode = node;
12306         node.ui.onSelectedChange(true);
12307         this.fireEvent("selectionchange", this, this.selNodes);
12308         return node;
12309     },
12310     
12311     /**
12312      * Deselect a node.
12313      * @param {TreeNode} node The node to unselect
12314      */
12315     unselect : function(node){
12316         if(this.selMap[node.id]){
12317             node.ui.onSelectedChange(false);
12318             var sn = this.selNodes;
12319             var index = -1;
12320             if(sn.indexOf){
12321                 index = sn.indexOf(node);
12322             }else{
12323                 for(var i = 0, len = sn.length; i < len; i++){
12324                     if(sn[i] == node){
12325                         index = i;
12326                         break;
12327                     }
12328                 }
12329             }
12330             if(index != -1){
12331                 this.selNodes.splice(index, 1);
12332             }
12333             delete this.selMap[node.id];
12334             this.fireEvent("selectionchange", this, this.selNodes);
12335         }
12336     },
12337     
12338     /**
12339      * Clear all selections
12340      */
12341     clearSelections : function(suppressEvent){
12342         var sn = this.selNodes;
12343         if(sn.length > 0){
12344             for(var i = 0, len = sn.length; i < len; i++){
12345                 sn[i].ui.onSelectedChange(false);
12346             }
12347             this.selNodes = [];
12348             this.selMap = {};
12349             if(suppressEvent !== true){
12350                 this.fireEvent("selectionchange", this, this.selNodes);
12351             }
12352         }
12353     },
12354     
12355     /**
12356      * Returns true if the node is selected
12357      * @param {TreeNode} node The node to check
12358      * @return {Boolean}
12359      */
12360     isSelected : function(node){
12361         return this.selMap[node.id] ? true : false;  
12362     },
12363     
12364     /**
12365      * Returns an array of the selected nodes
12366      * @return {Array}
12367      */
12368     getSelectedNodes : function(){
12369         return this.selNodes;    
12370     },
12371
12372     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12373
12374     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12375
12376     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12377 });/*
12378  * Based on:
12379  * Ext JS Library 1.1.1
12380  * Copyright(c) 2006-2007, Ext JS, LLC.
12381  *
12382  * Originally Released Under LGPL - original licence link has changed is not relivant.
12383  *
12384  * Fork - LGPL
12385  * <script type="text/javascript">
12386  */
12387  
12388 /**
12389  * @class Roo.tree.TreeNode
12390  * @extends Roo.data.Node
12391  * @cfg {String} text The text for this node
12392  * @cfg {Boolean} expanded true to start the node expanded
12393  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12394  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12395  * @cfg {Boolean} disabled true to start the node disabled
12396  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12397  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
12398  * @cfg {String} cls A css class to be added to the node
12399  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12400  * @cfg {String} href URL of the link used for the node (defaults to #)
12401  * @cfg {String} hrefTarget target frame for the link
12402  * @cfg {String} qtip An Ext QuickTip for the node
12403  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12404  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12405  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12406  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12407  * (defaults to undefined with no checkbox rendered)
12408  * @constructor
12409  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12410  */
12411 Roo.tree.TreeNode = function(attributes){
12412     attributes = attributes || {};
12413     if(typeof attributes == "string"){
12414         attributes = {text: attributes};
12415     }
12416     this.childrenRendered = false;
12417     this.rendered = false;
12418     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12419     this.expanded = attributes.expanded === true;
12420     this.isTarget = attributes.isTarget !== false;
12421     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12422     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12423
12424     /**
12425      * Read-only. The text for this node. To change it use setText().
12426      * @type String
12427      */
12428     this.text = attributes.text;
12429     /**
12430      * True if this node is disabled.
12431      * @type Boolean
12432      */
12433     this.disabled = attributes.disabled === true;
12434
12435     this.addEvents({
12436         /**
12437         * @event textchange
12438         * Fires when the text for this node is changed
12439         * @param {Node} this This node
12440         * @param {String} text The new text
12441         * @param {String} oldText The old text
12442         */
12443         "textchange" : true,
12444         /**
12445         * @event beforeexpand
12446         * Fires before this node is expanded, return false to cancel.
12447         * @param {Node} this This node
12448         * @param {Boolean} deep
12449         * @param {Boolean} anim
12450         */
12451         "beforeexpand" : true,
12452         /**
12453         * @event beforecollapse
12454         * Fires before this node is collapsed, return false to cancel.
12455         * @param {Node} this This node
12456         * @param {Boolean} deep
12457         * @param {Boolean} anim
12458         */
12459         "beforecollapse" : true,
12460         /**
12461         * @event expand
12462         * Fires when this node is expanded
12463         * @param {Node} this This node
12464         */
12465         "expand" : true,
12466         /**
12467         * @event disabledchange
12468         * Fires when the disabled status of this node changes
12469         * @param {Node} this This node
12470         * @param {Boolean} disabled
12471         */
12472         "disabledchange" : true,
12473         /**
12474         * @event collapse
12475         * Fires when this node is collapsed
12476         * @param {Node} this This node
12477         */
12478         "collapse" : true,
12479         /**
12480         * @event beforeclick
12481         * Fires before click processing. Return false to cancel the default action.
12482         * @param {Node} this This node
12483         * @param {Roo.EventObject} e The event object
12484         */
12485         "beforeclick":true,
12486         /**
12487         * @event checkchange
12488         * Fires when a node with a checkbox's checked property changes
12489         * @param {Node} this This node
12490         * @param {Boolean} checked
12491         */
12492         "checkchange":true,
12493         /**
12494         * @event click
12495         * Fires when this node is clicked
12496         * @param {Node} this This node
12497         * @param {Roo.EventObject} e The event object
12498         */
12499         "click":true,
12500         /**
12501         * @event dblclick
12502         * Fires when this node is double clicked
12503         * @param {Node} this This node
12504         * @param {Roo.EventObject} e The event object
12505         */
12506         "dblclick":true,
12507         /**
12508         * @event contextmenu
12509         * Fires when this node is right clicked
12510         * @param {Node} this This node
12511         * @param {Roo.EventObject} e The event object
12512         */
12513         "contextmenu":true,
12514         /**
12515         * @event beforechildrenrendered
12516         * Fires right before the child nodes for this node are rendered
12517         * @param {Node} this This node
12518         */
12519         "beforechildrenrendered":true
12520     });
12521
12522     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12523
12524     /**
12525      * Read-only. The UI for this node
12526      * @type TreeNodeUI
12527      */
12528     this.ui = new uiClass(this);
12529     
12530     // finally support items[]
12531     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12532         return;
12533     }
12534     
12535     
12536     Roo.each(this.attributes.items, function(c) {
12537         this.appendChild(Roo.factory(c,Roo.Tree));
12538     }, this);
12539     delete this.attributes.items;
12540     
12541     
12542     
12543 };
12544 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12545     preventHScroll: true,
12546     /**
12547      * Returns true if this node is expanded
12548      * @return {Boolean}
12549      */
12550     isExpanded : function(){
12551         return this.expanded;
12552     },
12553
12554     /**
12555      * Returns the UI object for this node
12556      * @return {TreeNodeUI}
12557      */
12558     getUI : function(){
12559         return this.ui;
12560     },
12561
12562     // private override
12563     setFirstChild : function(node){
12564         var of = this.firstChild;
12565         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12566         if(this.childrenRendered && of && node != of){
12567             of.renderIndent(true, true);
12568         }
12569         if(this.rendered){
12570             this.renderIndent(true, true);
12571         }
12572     },
12573
12574     // private override
12575     setLastChild : function(node){
12576         var ol = this.lastChild;
12577         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12578         if(this.childrenRendered && ol && node != ol){
12579             ol.renderIndent(true, true);
12580         }
12581         if(this.rendered){
12582             this.renderIndent(true, true);
12583         }
12584     },
12585
12586     // these methods are overridden to provide lazy rendering support
12587     // private override
12588     appendChild : function()
12589     {
12590         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12591         if(node && this.childrenRendered){
12592             node.render();
12593         }
12594         this.ui.updateExpandIcon();
12595         return node;
12596     },
12597
12598     // private override
12599     removeChild : function(node){
12600         this.ownerTree.getSelectionModel().unselect(node);
12601         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12602         // if it's been rendered remove dom node
12603         if(this.childrenRendered){
12604             node.ui.remove();
12605         }
12606         if(this.childNodes.length < 1){
12607             this.collapse(false, false);
12608         }else{
12609             this.ui.updateExpandIcon();
12610         }
12611         if(!this.firstChild) {
12612             this.childrenRendered = false;
12613         }
12614         return node;
12615     },
12616
12617     // private override
12618     insertBefore : function(node, refNode){
12619         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12620         if(newNode && refNode && this.childrenRendered){
12621             node.render();
12622         }
12623         this.ui.updateExpandIcon();
12624         return newNode;
12625     },
12626
12627     /**
12628      * Sets the text for this node
12629      * @param {String} text
12630      */
12631     setText : function(text){
12632         var oldText = this.text;
12633         this.text = text;
12634         this.attributes.text = text;
12635         if(this.rendered){ // event without subscribing
12636             this.ui.onTextChange(this, text, oldText);
12637         }
12638         this.fireEvent("textchange", this, text, oldText);
12639     },
12640
12641     /**
12642      * Triggers selection of this node
12643      */
12644     select : function(){
12645         this.getOwnerTree().getSelectionModel().select(this);
12646     },
12647
12648     /**
12649      * Triggers deselection of this node
12650      */
12651     unselect : function(){
12652         this.getOwnerTree().getSelectionModel().unselect(this);
12653     },
12654
12655     /**
12656      * Returns true if this node is selected
12657      * @return {Boolean}
12658      */
12659     isSelected : function(){
12660         return this.getOwnerTree().getSelectionModel().isSelected(this);
12661     },
12662
12663     /**
12664      * Expand this node.
12665      * @param {Boolean} deep (optional) True to expand all children as well
12666      * @param {Boolean} anim (optional) false to cancel the default animation
12667      * @param {Function} callback (optional) A callback to be called when
12668      * expanding this node completes (does not wait for deep expand to complete).
12669      * Called with 1 parameter, this node.
12670      */
12671     expand : function(deep, anim, callback){
12672         if(!this.expanded){
12673             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12674                 return;
12675             }
12676             if(!this.childrenRendered){
12677                 this.renderChildren();
12678             }
12679             this.expanded = true;
12680             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
12681                 this.ui.animExpand(function(){
12682                     this.fireEvent("expand", this);
12683                     if(typeof callback == "function"){
12684                         callback(this);
12685                     }
12686                     if(deep === true){
12687                         this.expandChildNodes(true);
12688                     }
12689                 }.createDelegate(this));
12690                 return;
12691             }else{
12692                 this.ui.expand();
12693                 this.fireEvent("expand", this);
12694                 if(typeof callback == "function"){
12695                     callback(this);
12696                 }
12697             }
12698         }else{
12699            if(typeof callback == "function"){
12700                callback(this);
12701            }
12702         }
12703         if(deep === true){
12704             this.expandChildNodes(true);
12705         }
12706     },
12707
12708     isHiddenRoot : function(){
12709         return this.isRoot && !this.getOwnerTree().rootVisible;
12710     },
12711
12712     /**
12713      * Collapse this node.
12714      * @param {Boolean} deep (optional) True to collapse all children as well
12715      * @param {Boolean} anim (optional) false to cancel the default animation
12716      */
12717     collapse : function(deep, anim){
12718         if(this.expanded && !this.isHiddenRoot()){
12719             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12720                 return;
12721             }
12722             this.expanded = false;
12723             if((this.getOwnerTree().animate && anim !== false) || anim){
12724                 this.ui.animCollapse(function(){
12725                     this.fireEvent("collapse", this);
12726                     if(deep === true){
12727                         this.collapseChildNodes(true);
12728                     }
12729                 }.createDelegate(this));
12730                 return;
12731             }else{
12732                 this.ui.collapse();
12733                 this.fireEvent("collapse", this);
12734             }
12735         }
12736         if(deep === true){
12737             var cs = this.childNodes;
12738             for(var i = 0, len = cs.length; i < len; i++) {
12739                 cs[i].collapse(true, false);
12740             }
12741         }
12742     },
12743
12744     // private
12745     delayedExpand : function(delay){
12746         if(!this.expandProcId){
12747             this.expandProcId = this.expand.defer(delay, this);
12748         }
12749     },
12750
12751     // private
12752     cancelExpand : function(){
12753         if(this.expandProcId){
12754             clearTimeout(this.expandProcId);
12755         }
12756         this.expandProcId = false;
12757     },
12758
12759     /**
12760      * Toggles expanded/collapsed state of the node
12761      */
12762     toggle : function(){
12763         if(this.expanded){
12764             this.collapse();
12765         }else{
12766             this.expand();
12767         }
12768     },
12769
12770     /**
12771      * Ensures all parent nodes are expanded
12772      */
12773     ensureVisible : function(callback){
12774         var tree = this.getOwnerTree();
12775         tree.expandPath(this.parentNode.getPath(), false, function(){
12776             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12777             Roo.callback(callback);
12778         }.createDelegate(this));
12779     },
12780
12781     /**
12782      * Expand all child nodes
12783      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12784      */
12785     expandChildNodes : function(deep){
12786         var cs = this.childNodes;
12787         for(var i = 0, len = cs.length; i < len; i++) {
12788                 cs[i].expand(deep);
12789         }
12790     },
12791
12792     /**
12793      * Collapse all child nodes
12794      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12795      */
12796     collapseChildNodes : function(deep){
12797         var cs = this.childNodes;
12798         for(var i = 0, len = cs.length; i < len; i++) {
12799                 cs[i].collapse(deep);
12800         }
12801     },
12802
12803     /**
12804      * Disables this node
12805      */
12806     disable : function(){
12807         this.disabled = true;
12808         this.unselect();
12809         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12810             this.ui.onDisableChange(this, true);
12811         }
12812         this.fireEvent("disabledchange", this, true);
12813     },
12814
12815     /**
12816      * Enables this node
12817      */
12818     enable : function(){
12819         this.disabled = false;
12820         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12821             this.ui.onDisableChange(this, false);
12822         }
12823         this.fireEvent("disabledchange", this, false);
12824     },
12825
12826     // private
12827     renderChildren : function(suppressEvent){
12828         if(suppressEvent !== false){
12829             this.fireEvent("beforechildrenrendered", this);
12830         }
12831         var cs = this.childNodes;
12832         for(var i = 0, len = cs.length; i < len; i++){
12833             cs[i].render(true);
12834         }
12835         this.childrenRendered = true;
12836     },
12837
12838     // private
12839     sort : function(fn, scope){
12840         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12841         if(this.childrenRendered){
12842             var cs = this.childNodes;
12843             for(var i = 0, len = cs.length; i < len; i++){
12844                 cs[i].render(true);
12845             }
12846         }
12847     },
12848
12849     // private
12850     render : function(bulkRender){
12851         this.ui.render(bulkRender);
12852         if(!this.rendered){
12853             this.rendered = true;
12854             if(this.expanded){
12855                 this.expanded = false;
12856                 this.expand(false, false);
12857             }
12858         }
12859     },
12860
12861     // private
12862     renderIndent : function(deep, refresh){
12863         if(refresh){
12864             this.ui.childIndent = null;
12865         }
12866         this.ui.renderIndent();
12867         if(deep === true && this.childrenRendered){
12868             var cs = this.childNodes;
12869             for(var i = 0, len = cs.length; i < len; i++){
12870                 cs[i].renderIndent(true, refresh);
12871             }
12872         }
12873     }
12874 });/*
12875  * Based on:
12876  * Ext JS Library 1.1.1
12877  * Copyright(c) 2006-2007, Ext JS, LLC.
12878  *
12879  * Originally Released Under LGPL - original licence link has changed is not relivant.
12880  *
12881  * Fork - LGPL
12882  * <script type="text/javascript">
12883  */
12884  
12885 /**
12886  * @class Roo.tree.AsyncTreeNode
12887  * @extends Roo.tree.TreeNode
12888  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12889  * @constructor
12890  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12891  */
12892  Roo.tree.AsyncTreeNode = function(config){
12893     this.loaded = false;
12894     this.loading = false;
12895     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12896     /**
12897     * @event beforeload
12898     * Fires before this node is loaded, return false to cancel
12899     * @param {Node} this This node
12900     */
12901     this.addEvents({'beforeload':true, 'load': true});
12902     /**
12903     * @event load
12904     * Fires when this node is loaded
12905     * @param {Node} this This node
12906     */
12907     /**
12908      * The loader used by this node (defaults to using the tree's defined loader)
12909      * @type TreeLoader
12910      * @property loader
12911      */
12912 };
12913 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12914     expand : function(deep, anim, callback){
12915         if(this.loading){ // if an async load is already running, waiting til it's done
12916             var timer;
12917             var f = function(){
12918                 if(!this.loading){ // done loading
12919                     clearInterval(timer);
12920                     this.expand(deep, anim, callback);
12921                 }
12922             }.createDelegate(this);
12923             timer = setInterval(f, 200);
12924             return;
12925         }
12926         if(!this.loaded){
12927             if(this.fireEvent("beforeload", this) === false){
12928                 return;
12929             }
12930             this.loading = true;
12931             this.ui.beforeLoad(this);
12932             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12933             if(loader){
12934                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12935                 return;
12936             }
12937         }
12938         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12939     },
12940     
12941     /**
12942      * Returns true if this node is currently loading
12943      * @return {Boolean}
12944      */
12945     isLoading : function(){
12946         return this.loading;  
12947     },
12948     
12949     loadComplete : function(deep, anim, callback){
12950         this.loading = false;
12951         this.loaded = true;
12952         this.ui.afterLoad(this);
12953         this.fireEvent("load", this);
12954         this.expand(deep, anim, callback);
12955     },
12956     
12957     /**
12958      * Returns true if this node has been loaded
12959      * @return {Boolean}
12960      */
12961     isLoaded : function(){
12962         return this.loaded;
12963     },
12964     
12965     hasChildNodes : function(){
12966         if(!this.isLeaf() && !this.loaded){
12967             return true;
12968         }else{
12969             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12970         }
12971     },
12972
12973     /**
12974      * Trigger a reload for this node
12975      * @param {Function} callback
12976      */
12977     reload : function(callback){
12978         this.collapse(false, false);
12979         while(this.firstChild){
12980             this.removeChild(this.firstChild);
12981         }
12982         this.childrenRendered = false;
12983         this.loaded = false;
12984         if(this.isHiddenRoot()){
12985             this.expanded = false;
12986         }
12987         this.expand(false, false, callback);
12988     }
12989 });/*
12990  * Based on:
12991  * Ext JS Library 1.1.1
12992  * Copyright(c) 2006-2007, Ext JS, LLC.
12993  *
12994  * Originally Released Under LGPL - original licence link has changed is not relivant.
12995  *
12996  * Fork - LGPL
12997  * <script type="text/javascript">
12998  */
12999  
13000 /**
13001  * @class Roo.tree.TreeNodeUI
13002  * @constructor
13003  * @param {Object} node The node to render
13004  * The TreeNode UI implementation is separate from the
13005  * tree implementation. Unless you are customizing the tree UI,
13006  * you should never have to use this directly.
13007  */
13008 Roo.tree.TreeNodeUI = function(node){
13009     this.node = node;
13010     this.rendered = false;
13011     this.animating = false;
13012     this.emptyIcon = Roo.BLANK_IMAGE_URL;
13013 };
13014
13015 Roo.tree.TreeNodeUI.prototype = {
13016     removeChild : function(node){
13017         if(this.rendered){
13018             this.ctNode.removeChild(node.ui.getEl());
13019         }
13020     },
13021
13022     beforeLoad : function(){
13023          this.addClass("x-tree-node-loading");
13024     },
13025
13026     afterLoad : function(){
13027          this.removeClass("x-tree-node-loading");
13028     },
13029
13030     onTextChange : function(node, text, oldText){
13031         if(this.rendered){
13032             this.textNode.innerHTML = text;
13033         }
13034     },
13035
13036     onDisableChange : function(node, state){
13037         this.disabled = state;
13038         if(state){
13039             this.addClass("x-tree-node-disabled");
13040         }else{
13041             this.removeClass("x-tree-node-disabled");
13042         }
13043     },
13044
13045     onSelectedChange : function(state){
13046         if(state){
13047             this.focus();
13048             this.addClass("x-tree-selected");
13049         }else{
13050             //this.blur();
13051             this.removeClass("x-tree-selected");
13052         }
13053     },
13054
13055     onMove : function(tree, node, oldParent, newParent, index, refNode){
13056         this.childIndent = null;
13057         if(this.rendered){
13058             var targetNode = newParent.ui.getContainer();
13059             if(!targetNode){//target not rendered
13060                 this.holder = document.createElement("div");
13061                 this.holder.appendChild(this.wrap);
13062                 return;
13063             }
13064             var insertBefore = refNode ? refNode.ui.getEl() : null;
13065             if(insertBefore){
13066                 targetNode.insertBefore(this.wrap, insertBefore);
13067             }else{
13068                 targetNode.appendChild(this.wrap);
13069             }
13070             this.node.renderIndent(true);
13071         }
13072     },
13073
13074     addClass : function(cls){
13075         if(this.elNode){
13076             Roo.fly(this.elNode).addClass(cls);
13077         }
13078     },
13079
13080     removeClass : function(cls){
13081         if(this.elNode){
13082             Roo.fly(this.elNode).removeClass(cls);
13083         }
13084     },
13085
13086     remove : function(){
13087         if(this.rendered){
13088             this.holder = document.createElement("div");
13089             this.holder.appendChild(this.wrap);
13090         }
13091     },
13092
13093     fireEvent : function(){
13094         return this.node.fireEvent.apply(this.node, arguments);
13095     },
13096
13097     initEvents : function(){
13098         this.node.on("move", this.onMove, this);
13099         var E = Roo.EventManager;
13100         var a = this.anchor;
13101
13102         var el = Roo.fly(a, '_treeui');
13103
13104         if(Roo.isOpera){ // opera render bug ignores the CSS
13105             el.setStyle("text-decoration", "none");
13106         }
13107
13108         el.on("click", this.onClick, this);
13109         el.on("dblclick", this.onDblClick, this);
13110
13111         if(this.checkbox){
13112             Roo.EventManager.on(this.checkbox,
13113                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
13114         }
13115
13116         el.on("contextmenu", this.onContextMenu, this);
13117
13118         var icon = Roo.fly(this.iconNode);
13119         icon.on("click", this.onClick, this);
13120         icon.on("dblclick", this.onDblClick, this);
13121         icon.on("contextmenu", this.onContextMenu, this);
13122         E.on(this.ecNode, "click", this.ecClick, this, true);
13123
13124         if(this.node.disabled){
13125             this.addClass("x-tree-node-disabled");
13126         }
13127         if(this.node.hidden){
13128             this.addClass("x-tree-node-disabled");
13129         }
13130         var ot = this.node.getOwnerTree();
13131         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
13132         if(dd && (!this.node.isRoot || ot.rootVisible)){
13133             Roo.dd.Registry.register(this.elNode, {
13134                 node: this.node,
13135                 handles: this.getDDHandles(),
13136                 isHandle: false
13137             });
13138         }
13139     },
13140
13141     getDDHandles : function(){
13142         return [this.iconNode, this.textNode];
13143     },
13144
13145     hide : function(){
13146         if(this.rendered){
13147             this.wrap.style.display = "none";
13148         }
13149     },
13150
13151     show : function(){
13152         if(this.rendered){
13153             this.wrap.style.display = "";
13154         }
13155     },
13156
13157     onContextMenu : function(e){
13158         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
13159             e.preventDefault();
13160             this.focus();
13161             this.fireEvent("contextmenu", this.node, e);
13162         }
13163     },
13164
13165     onClick : function(e){
13166         if(this.dropping){
13167             e.stopEvent();
13168             return;
13169         }
13170         if(this.fireEvent("beforeclick", this.node, e) !== false){
13171             if(!this.disabled && this.node.attributes.href){
13172                 this.fireEvent("click", this.node, e);
13173                 return;
13174             }
13175             e.preventDefault();
13176             if(this.disabled){
13177                 return;
13178             }
13179
13180             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
13181                 this.node.toggle();
13182             }
13183
13184             this.fireEvent("click", this.node, e);
13185         }else{
13186             e.stopEvent();
13187         }
13188     },
13189
13190     onDblClick : function(e){
13191         e.preventDefault();
13192         if(this.disabled){
13193             return;
13194         }
13195         if(this.checkbox){
13196             this.toggleCheck();
13197         }
13198         if(!this.animating && this.node.hasChildNodes()){
13199             this.node.toggle();
13200         }
13201         this.fireEvent("dblclick", this.node, e);
13202     },
13203
13204     onCheckChange : function(){
13205         var checked = this.checkbox.checked;
13206         this.node.attributes.checked = checked;
13207         this.fireEvent('checkchange', this.node, checked);
13208     },
13209
13210     ecClick : function(e){
13211         if(!this.animating && this.node.hasChildNodes()){
13212             this.node.toggle();
13213         }
13214     },
13215
13216     startDrop : function(){
13217         this.dropping = true;
13218     },
13219
13220     // delayed drop so the click event doesn't get fired on a drop
13221     endDrop : function(){
13222        setTimeout(function(){
13223            this.dropping = false;
13224        }.createDelegate(this), 50);
13225     },
13226
13227     expand : function(){
13228         this.updateExpandIcon();
13229         this.ctNode.style.display = "";
13230     },
13231
13232     focus : function(){
13233         if(!this.node.preventHScroll){
13234             try{this.anchor.focus();
13235             }catch(e){}
13236         }else if(!Roo.isIE){
13237             try{
13238                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
13239                 var l = noscroll.scrollLeft;
13240                 this.anchor.focus();
13241                 noscroll.scrollLeft = l;
13242             }catch(e){}
13243         }
13244     },
13245
13246     toggleCheck : function(value){
13247         var cb = this.checkbox;
13248         if(cb){
13249             cb.checked = (value === undefined ? !cb.checked : value);
13250         }
13251     },
13252
13253     blur : function(){
13254         try{
13255             this.anchor.blur();
13256         }catch(e){}
13257     },
13258
13259     animExpand : function(callback){
13260         var ct = Roo.get(this.ctNode);
13261         ct.stopFx();
13262         if(!this.node.hasChildNodes()){
13263             this.updateExpandIcon();
13264             this.ctNode.style.display = "";
13265             Roo.callback(callback);
13266             return;
13267         }
13268         this.animating = true;
13269         this.updateExpandIcon();
13270
13271         ct.slideIn('t', {
13272            callback : function(){
13273                this.animating = false;
13274                Roo.callback(callback);
13275             },
13276             scope: this,
13277             duration: this.node.ownerTree.duration || .25
13278         });
13279     },
13280
13281     highlight : function(){
13282         var tree = this.node.getOwnerTree();
13283         Roo.fly(this.wrap).highlight(
13284             tree.hlColor || "C3DAF9",
13285             {endColor: tree.hlBaseColor}
13286         );
13287     },
13288
13289     collapse : function(){
13290         this.updateExpandIcon();
13291         this.ctNode.style.display = "none";
13292     },
13293
13294     animCollapse : function(callback){
13295         var ct = Roo.get(this.ctNode);
13296         ct.enableDisplayMode('block');
13297         ct.stopFx();
13298
13299         this.animating = true;
13300         this.updateExpandIcon();
13301
13302         ct.slideOut('t', {
13303             callback : function(){
13304                this.animating = false;
13305                Roo.callback(callback);
13306             },
13307             scope: this,
13308             duration: this.node.ownerTree.duration || .25
13309         });
13310     },
13311
13312     getContainer : function(){
13313         return this.ctNode;
13314     },
13315
13316     getEl : function(){
13317         return this.wrap;
13318     },
13319
13320     appendDDGhost : function(ghostNode){
13321         ghostNode.appendChild(this.elNode.cloneNode(true));
13322     },
13323
13324     getDDRepairXY : function(){
13325         return Roo.lib.Dom.getXY(this.iconNode);
13326     },
13327
13328     onRender : function(){
13329         this.render();
13330     },
13331
13332     render : function(bulkRender){
13333         var n = this.node, a = n.attributes;
13334         var targetNode = n.parentNode ?
13335               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
13336
13337         if(!this.rendered){
13338             this.rendered = true;
13339
13340             this.renderElements(n, a, targetNode, bulkRender);
13341
13342             if(a.qtip){
13343                if(this.textNode.setAttributeNS){
13344                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
13345                    if(a.qtipTitle){
13346                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
13347                    }
13348                }else{
13349                    this.textNode.setAttribute("ext:qtip", a.qtip);
13350                    if(a.qtipTitle){
13351                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
13352                    }
13353                }
13354             }else if(a.qtipCfg){
13355                 a.qtipCfg.target = Roo.id(this.textNode);
13356                 Roo.QuickTips.register(a.qtipCfg);
13357             }
13358             this.initEvents();
13359             if(!this.node.expanded){
13360                 this.updateExpandIcon();
13361             }
13362         }else{
13363             if(bulkRender === true) {
13364                 targetNode.appendChild(this.wrap);
13365             }
13366         }
13367     },
13368
13369     renderElements : function(n, a, targetNode, bulkRender)
13370     {
13371         // add some indent caching, this helps performance when rendering a large tree
13372         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13373         var t = n.getOwnerTree();
13374         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13375         if (typeof(n.attributes.html) != 'undefined') {
13376             txt = n.attributes.html;
13377         }
13378         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
13379         var cb = typeof a.checked == 'boolean';
13380         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13381         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13382             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13383             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13384             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13385             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13386             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13387              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13388                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13389             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13390             "</li>"];
13391
13392         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13393             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13394                                 n.nextSibling.ui.getEl(), buf.join(""));
13395         }else{
13396             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13397         }
13398
13399         this.elNode = this.wrap.childNodes[0];
13400         this.ctNode = this.wrap.childNodes[1];
13401         var cs = this.elNode.childNodes;
13402         this.indentNode = cs[0];
13403         this.ecNode = cs[1];
13404         this.iconNode = cs[2];
13405         var index = 3;
13406         if(cb){
13407             this.checkbox = cs[3];
13408             index++;
13409         }
13410         this.anchor = cs[index];
13411         this.textNode = cs[index].firstChild;
13412     },
13413
13414     getAnchor : function(){
13415         return this.anchor;
13416     },
13417
13418     getTextEl : function(){
13419         return this.textNode;
13420     },
13421
13422     getIconEl : function(){
13423         return this.iconNode;
13424     },
13425
13426     isChecked : function(){
13427         return this.checkbox ? this.checkbox.checked : false;
13428     },
13429
13430     updateExpandIcon : function(){
13431         if(this.rendered){
13432             var n = this.node, c1, c2;
13433             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13434             var hasChild = n.hasChildNodes();
13435             if(hasChild){
13436                 if(n.expanded){
13437                     cls += "-minus";
13438                     c1 = "x-tree-node-collapsed";
13439                     c2 = "x-tree-node-expanded";
13440                 }else{
13441                     cls += "-plus";
13442                     c1 = "x-tree-node-expanded";
13443                     c2 = "x-tree-node-collapsed";
13444                 }
13445                 if(this.wasLeaf){
13446                     this.removeClass("x-tree-node-leaf");
13447                     this.wasLeaf = false;
13448                 }
13449                 if(this.c1 != c1 || this.c2 != c2){
13450                     Roo.fly(this.elNode).replaceClass(c1, c2);
13451                     this.c1 = c1; this.c2 = c2;
13452                 }
13453             }else{
13454                 // this changes non-leafs into leafs if they have no children.
13455                 // it's not very rational behaviour..
13456                 
13457                 if(!this.wasLeaf && this.node.leaf){
13458                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13459                     delete this.c1;
13460                     delete this.c2;
13461                     this.wasLeaf = true;
13462                 }
13463             }
13464             var ecc = "x-tree-ec-icon "+cls;
13465             if(this.ecc != ecc){
13466                 this.ecNode.className = ecc;
13467                 this.ecc = ecc;
13468             }
13469         }
13470     },
13471
13472     getChildIndent : function(){
13473         if(!this.childIndent){
13474             var buf = [];
13475             var p = this.node;
13476             while(p){
13477                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13478                     if(!p.isLast()) {
13479                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13480                     } else {
13481                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13482                     }
13483                 }
13484                 p = p.parentNode;
13485             }
13486             this.childIndent = buf.join("");
13487         }
13488         return this.childIndent;
13489     },
13490
13491     renderIndent : function(){
13492         if(this.rendered){
13493             var indent = "";
13494             var p = this.node.parentNode;
13495             if(p){
13496                 indent = p.ui.getChildIndent();
13497             }
13498             if(this.indentMarkup != indent){ // don't rerender if not required
13499                 this.indentNode.innerHTML = indent;
13500                 this.indentMarkup = indent;
13501             }
13502             this.updateExpandIcon();
13503         }
13504     }
13505 };
13506
13507 Roo.tree.RootTreeNodeUI = function(){
13508     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13509 };
13510 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13511     render : function(){
13512         if(!this.rendered){
13513             var targetNode = this.node.ownerTree.innerCt.dom;
13514             this.node.expanded = true;
13515             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13516             this.wrap = this.ctNode = targetNode.firstChild;
13517         }
13518     },
13519     collapse : function(){
13520     },
13521     expand : function(){
13522     }
13523 });/*
13524  * Based on:
13525  * Ext JS Library 1.1.1
13526  * Copyright(c) 2006-2007, Ext JS, LLC.
13527  *
13528  * Originally Released Under LGPL - original licence link has changed is not relivant.
13529  *
13530  * Fork - LGPL
13531  * <script type="text/javascript">
13532  */
13533 /**
13534  * @class Roo.tree.TreeLoader
13535  * @extends Roo.util.Observable
13536  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13537  * nodes from a specified URL. The response must be a javascript Array definition
13538  * who's elements are node definition objects. eg:
13539  * <pre><code>
13540 {  success : true,
13541    data :      [
13542    
13543     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13544     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13545     ]
13546 }
13547
13548
13549 </code></pre>
13550  * <br><br>
13551  * The old style respose with just an array is still supported, but not recommended.
13552  * <br><br>
13553  *
13554  * A server request is sent, and child nodes are loaded only when a node is expanded.
13555  * The loading node's id is passed to the server under the parameter name "node" to
13556  * enable the server to produce the correct child nodes.
13557  * <br><br>
13558  * To pass extra parameters, an event handler may be attached to the "beforeload"
13559  * event, and the parameters specified in the TreeLoader's baseParams property:
13560  * <pre><code>
13561     myTreeLoader.on("beforeload", function(treeLoader, node) {
13562         this.baseParams.category = node.attributes.category;
13563     }, this);
13564 </code></pre><
13565  * This would pass an HTTP parameter called "category" to the server containing
13566  * the value of the Node's "category" attribute.
13567  * @constructor
13568  * Creates a new Treeloader.
13569  * @param {Object} config A config object containing config properties.
13570  */
13571 Roo.tree.TreeLoader = function(config){
13572     this.baseParams = {};
13573     this.requestMethod = "POST";
13574     Roo.apply(this, config);
13575
13576     this.addEvents({
13577     
13578         /**
13579          * @event beforeload
13580          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13581          * @param {Object} This TreeLoader object.
13582          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13583          * @param {Object} callback The callback function specified in the {@link #load} call.
13584          */
13585         beforeload : true,
13586         /**
13587          * @event load
13588          * Fires when the node has been successfuly loaded.
13589          * @param {Object} This TreeLoader object.
13590          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13591          * @param {Object} response The response object containing the data from the server.
13592          */
13593         load : true,
13594         /**
13595          * @event loadexception
13596          * Fires if the network request failed.
13597          * @param {Object} This TreeLoader object.
13598          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13599          * @param {Object} response The response object containing the data from the server.
13600          */
13601         loadexception : true,
13602         /**
13603          * @event create
13604          * Fires before a node is created, enabling you to return custom Node types 
13605          * @param {Object} This TreeLoader object.
13606          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13607          */
13608         create : true
13609     });
13610
13611     Roo.tree.TreeLoader.superclass.constructor.call(this);
13612 };
13613
13614 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13615     /**
13616     * @cfg {String} dataUrl The URL from which to request a Json string which
13617     * specifies an array of node definition object representing the child nodes
13618     * to be loaded.
13619     */
13620     /**
13621     * @cfg {String} requestMethod either GET or POST
13622     * defaults to POST (due to BC)
13623     * to be loaded.
13624     */
13625     /**
13626     * @cfg {Object} baseParams (optional) An object containing properties which
13627     * specify HTTP parameters to be passed to each request for child nodes.
13628     */
13629     /**
13630     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13631     * created by this loader. If the attributes sent by the server have an attribute in this object,
13632     * they take priority.
13633     */
13634     /**
13635     * @cfg {Object} uiProviders (optional) An object containing properties which
13636     * 
13637     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13638     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13639     * <i>uiProvider</i> attribute of a returned child node is a string rather
13640     * than a reference to a TreeNodeUI implementation, this that string value
13641     * is used as a property name in the uiProviders object. You can define the provider named
13642     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13643     */
13644     uiProviders : {},
13645
13646     /**
13647     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13648     * child nodes before loading.
13649     */
13650     clearOnLoad : true,
13651
13652     /**
13653     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13654     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13655     * Grid query { data : [ .....] }
13656     */
13657     
13658     root : false,
13659      /**
13660     * @cfg {String} queryParam (optional) 
13661     * Name of the query as it will be passed on the querystring (defaults to 'node')
13662     * eg. the request will be ?node=[id]
13663     */
13664     
13665     
13666     queryParam: false,
13667     
13668     /**
13669      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13670      * This is called automatically when a node is expanded, but may be used to reload
13671      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13672      * @param {Roo.tree.TreeNode} node
13673      * @param {Function} callback
13674      */
13675     load : function(node, callback){
13676         if(this.clearOnLoad){
13677             while(node.firstChild){
13678                 node.removeChild(node.firstChild);
13679             }
13680         }
13681         if(node.attributes.children){ // preloaded json children
13682             var cs = node.attributes.children;
13683             for(var i = 0, len = cs.length; i < len; i++){
13684                 node.appendChild(this.createNode(cs[i]));
13685             }
13686             if(typeof callback == "function"){
13687                 callback();
13688             }
13689         }else if(this.dataUrl){
13690             this.requestData(node, callback);
13691         }
13692     },
13693
13694     getParams: function(node){
13695         var buf = [], bp = this.baseParams;
13696         for(var key in bp){
13697             if(typeof bp[key] != "function"){
13698                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13699             }
13700         }
13701         var n = this.queryParam === false ? 'node' : this.queryParam;
13702         buf.push(n + "=", encodeURIComponent(node.id));
13703         return buf.join("");
13704     },
13705
13706     requestData : function(node, callback){
13707         if(this.fireEvent("beforeload", this, node, callback) !== false){
13708             this.transId = Roo.Ajax.request({
13709                 method:this.requestMethod,
13710                 url: this.dataUrl||this.url,
13711                 success: this.handleResponse,
13712                 failure: this.handleFailure,
13713                 scope: this,
13714                 argument: {callback: callback, node: node},
13715                 params: this.getParams(node)
13716             });
13717         }else{
13718             // if the load is cancelled, make sure we notify
13719             // the node that we are done
13720             if(typeof callback == "function"){
13721                 callback();
13722             }
13723         }
13724     },
13725
13726     isLoading : function(){
13727         return this.transId ? true : false;
13728     },
13729
13730     abort : function(){
13731         if(this.isLoading()){
13732             Roo.Ajax.abort(this.transId);
13733         }
13734     },
13735
13736     // private
13737     createNode : function(attr)
13738     {
13739         // apply baseAttrs, nice idea Corey!
13740         if(this.baseAttrs){
13741             Roo.applyIf(attr, this.baseAttrs);
13742         }
13743         if(this.applyLoader !== false){
13744             attr.loader = this;
13745         }
13746         // uiProvider = depreciated..
13747         
13748         if(typeof(attr.uiProvider) == 'string'){
13749            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13750                 /**  eval:var:attr */ eval(attr.uiProvider);
13751         }
13752         if(typeof(this.uiProviders['default']) != 'undefined') {
13753             attr.uiProvider = this.uiProviders['default'];
13754         }
13755         
13756         this.fireEvent('create', this, attr);
13757         
13758         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13759         return(attr.leaf ?
13760                         new Roo.tree.TreeNode(attr) :
13761                         new Roo.tree.AsyncTreeNode(attr));
13762     },
13763
13764     processResponse : function(response, node, callback)
13765     {
13766         var json = response.responseText;
13767         try {
13768             
13769             var o = Roo.decode(json);
13770             
13771             if (this.root === false && typeof(o.success) != undefined) {
13772                 this.root = 'data'; // the default behaviour for list like data..
13773                 }
13774                 
13775             if (this.root !== false &&  !o.success) {
13776                 // it's a failure condition.
13777                 var a = response.argument;
13778                 this.fireEvent("loadexception", this, a.node, response);
13779                 Roo.log("Load failed - should have a handler really");
13780                 return;
13781             }
13782             
13783             
13784             
13785             if (this.root !== false) {
13786                  o = o[this.root];
13787             }
13788             
13789             for(var i = 0, len = o.length; i < len; i++){
13790                 var n = this.createNode(o[i]);
13791                 if(n){
13792                     node.appendChild(n);
13793                 }
13794             }
13795             if(typeof callback == "function"){
13796                 callback(this, node);
13797             }
13798         }catch(e){
13799             this.handleFailure(response);
13800         }
13801     },
13802
13803     handleResponse : function(response){
13804         this.transId = false;
13805         var a = response.argument;
13806         this.processResponse(response, a.node, a.callback);
13807         this.fireEvent("load", this, a.node, response);
13808     },
13809
13810     handleFailure : function(response)
13811     {
13812         // should handle failure better..
13813         this.transId = false;
13814         var a = response.argument;
13815         this.fireEvent("loadexception", this, a.node, response);
13816         if(typeof a.callback == "function"){
13817             a.callback(this, a.node);
13818         }
13819     }
13820 });/*
13821  * Based on:
13822  * Ext JS Library 1.1.1
13823  * Copyright(c) 2006-2007, Ext JS, LLC.
13824  *
13825  * Originally Released Under LGPL - original licence link has changed is not relivant.
13826  *
13827  * Fork - LGPL
13828  * <script type="text/javascript">
13829  */
13830
13831 /**
13832 * @class Roo.tree.TreeFilter
13833 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13834 * @param {TreePanel} tree
13835 * @param {Object} config (optional)
13836  */
13837 Roo.tree.TreeFilter = function(tree, config){
13838     this.tree = tree;
13839     this.filtered = {};
13840     Roo.apply(this, config);
13841 };
13842
13843 Roo.tree.TreeFilter.prototype = {
13844     clearBlank:false,
13845     reverse:false,
13846     autoClear:false,
13847     remove:false,
13848
13849      /**
13850      * Filter the data by a specific attribute.
13851      * @param {String/RegExp} value Either string that the attribute value
13852      * should start with or a RegExp to test against the attribute
13853      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13854      * @param {TreeNode} startNode (optional) The node to start the filter at.
13855      */
13856     filter : function(value, attr, startNode){
13857         attr = attr || "text";
13858         var f;
13859         if(typeof value == "string"){
13860             var vlen = value.length;
13861             // auto clear empty filter
13862             if(vlen == 0 && this.clearBlank){
13863                 this.clear();
13864                 return;
13865             }
13866             value = value.toLowerCase();
13867             f = function(n){
13868                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13869             };
13870         }else if(value.exec){ // regex?
13871             f = function(n){
13872                 return value.test(n.attributes[attr]);
13873             };
13874         }else{
13875             throw 'Illegal filter type, must be string or regex';
13876         }
13877         this.filterBy(f, null, startNode);
13878         },
13879
13880     /**
13881      * Filter by a function. The passed function will be called with each
13882      * node in the tree (or from the startNode). If the function returns true, the node is kept
13883      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13884      * @param {Function} fn The filter function
13885      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13886      */
13887     filterBy : function(fn, scope, startNode){
13888         startNode = startNode || this.tree.root;
13889         if(this.autoClear){
13890             this.clear();
13891         }
13892         var af = this.filtered, rv = this.reverse;
13893         var f = function(n){
13894             if(n == startNode){
13895                 return true;
13896             }
13897             if(af[n.id]){
13898                 return false;
13899             }
13900             var m = fn.call(scope || n, n);
13901             if(!m || rv){
13902                 af[n.id] = n;
13903                 n.ui.hide();
13904                 return false;
13905             }
13906             return true;
13907         };
13908         startNode.cascade(f);
13909         if(this.remove){
13910            for(var id in af){
13911                if(typeof id != "function"){
13912                    var n = af[id];
13913                    if(n && n.parentNode){
13914                        n.parentNode.removeChild(n);
13915                    }
13916                }
13917            }
13918         }
13919     },
13920
13921     /**
13922      * Clears the current filter. Note: with the "remove" option
13923      * set a filter cannot be cleared.
13924      */
13925     clear : function(){
13926         var t = this.tree;
13927         var af = this.filtered;
13928         for(var id in af){
13929             if(typeof id != "function"){
13930                 var n = af[id];
13931                 if(n){
13932                     n.ui.show();
13933                 }
13934             }
13935         }
13936         this.filtered = {};
13937     }
13938 };
13939 /*
13940  * Based on:
13941  * Ext JS Library 1.1.1
13942  * Copyright(c) 2006-2007, Ext JS, LLC.
13943  *
13944  * Originally Released Under LGPL - original licence link has changed is not relivant.
13945  *
13946  * Fork - LGPL
13947  * <script type="text/javascript">
13948  */
13949  
13950
13951 /**
13952  * @class Roo.tree.TreeSorter
13953  * Provides sorting of nodes in a TreePanel
13954  * 
13955  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13956  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13957  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13958  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13959  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13960  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13961  * @constructor
13962  * @param {TreePanel} tree
13963  * @param {Object} config
13964  */
13965 Roo.tree.TreeSorter = function(tree, config){
13966     Roo.apply(this, config);
13967     tree.on("beforechildrenrendered", this.doSort, this);
13968     tree.on("append", this.updateSort, this);
13969     tree.on("insert", this.updateSort, this);
13970     
13971     var dsc = this.dir && this.dir.toLowerCase() == "desc";
13972     var p = this.property || "text";
13973     var sortType = this.sortType;
13974     var fs = this.folderSort;
13975     var cs = this.caseSensitive === true;
13976     var leafAttr = this.leafAttr || 'leaf';
13977
13978     this.sortFn = function(n1, n2){
13979         if(fs){
13980             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13981                 return 1;
13982             }
13983             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13984                 return -1;
13985             }
13986         }
13987         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13988         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13989         if(v1 < v2){
13990                         return dsc ? +1 : -1;
13991                 }else if(v1 > v2){
13992                         return dsc ? -1 : +1;
13993         }else{
13994                 return 0;
13995         }
13996     };
13997 };
13998
13999 Roo.tree.TreeSorter.prototype = {
14000     doSort : function(node){
14001         node.sort(this.sortFn);
14002     },
14003     
14004     compareNodes : function(n1, n2){
14005         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
14006     },
14007     
14008     updateSort : function(tree, node){
14009         if(node.childrenRendered){
14010             this.doSort.defer(1, this, [node]);
14011         }
14012     }
14013 };/*
14014  * Based on:
14015  * Ext JS Library 1.1.1
14016  * Copyright(c) 2006-2007, Ext JS, LLC.
14017  *
14018  * Originally Released Under LGPL - original licence link has changed is not relivant.
14019  *
14020  * Fork - LGPL
14021  * <script type="text/javascript">
14022  */
14023
14024 if(Roo.dd.DropZone){
14025     
14026 Roo.tree.TreeDropZone = function(tree, config){
14027     this.allowParentInsert = false;
14028     this.allowContainerDrop = false;
14029     this.appendOnly = false;
14030     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
14031     this.tree = tree;
14032     this.lastInsertClass = "x-tree-no-status";
14033     this.dragOverData = {};
14034 };
14035
14036 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
14037     ddGroup : "TreeDD",
14038     scroll:  true,
14039     
14040     expandDelay : 1000,
14041     
14042     expandNode : function(node){
14043         if(node.hasChildNodes() && !node.isExpanded()){
14044             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
14045         }
14046     },
14047     
14048     queueExpand : function(node){
14049         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
14050     },
14051     
14052     cancelExpand : function(){
14053         if(this.expandProcId){
14054             clearTimeout(this.expandProcId);
14055             this.expandProcId = false;
14056         }
14057     },
14058     
14059     isValidDropPoint : function(n, pt, dd, e, data){
14060         if(!n || !data){ return false; }
14061         var targetNode = n.node;
14062         var dropNode = data.node;
14063         // default drop rules
14064         if(!(targetNode && targetNode.isTarget && pt)){
14065             return false;
14066         }
14067         if(pt == "append" && targetNode.allowChildren === false){
14068             return false;
14069         }
14070         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
14071             return false;
14072         }
14073         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
14074             return false;
14075         }
14076         // reuse the object
14077         var overEvent = this.dragOverData;
14078         overEvent.tree = this.tree;
14079         overEvent.target = targetNode;
14080         overEvent.data = data;
14081         overEvent.point = pt;
14082         overEvent.source = dd;
14083         overEvent.rawEvent = e;
14084         overEvent.dropNode = dropNode;
14085         overEvent.cancel = false;  
14086         var result = this.tree.fireEvent("nodedragover", overEvent);
14087         return overEvent.cancel === false && result !== false;
14088     },
14089     
14090     getDropPoint : function(e, n, dd)
14091     {
14092         var tn = n.node;
14093         if(tn.isRoot){
14094             return tn.allowChildren !== false ? "append" : false; // always append for root
14095         }
14096         var dragEl = n.ddel;
14097         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
14098         var y = Roo.lib.Event.getPageY(e);
14099         //var noAppend = tn.allowChildren === false || tn.isLeaf();
14100         
14101         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
14102         var noAppend = tn.allowChildren === false;
14103         if(this.appendOnly || tn.parentNode.allowChildren === false){
14104             return noAppend ? false : "append";
14105         }
14106         var noBelow = false;
14107         if(!this.allowParentInsert){
14108             noBelow = tn.hasChildNodes() && tn.isExpanded();
14109         }
14110         var q = (b - t) / (noAppend ? 2 : 3);
14111         if(y >= t && y < (t + q)){
14112             return "above";
14113         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
14114             return "below";
14115         }else{
14116             return "append";
14117         }
14118     },
14119     
14120     onNodeEnter : function(n, dd, e, data)
14121     {
14122         this.cancelExpand();
14123     },
14124     
14125     onNodeOver : function(n, dd, e, data)
14126     {
14127        
14128         var pt = this.getDropPoint(e, n, dd);
14129         var node = n.node;
14130         
14131         // auto node expand check
14132         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
14133             this.queueExpand(node);
14134         }else if(pt != "append"){
14135             this.cancelExpand();
14136         }
14137         
14138         // set the insert point style on the target node
14139         var returnCls = this.dropNotAllowed;
14140         if(this.isValidDropPoint(n, pt, dd, e, data)){
14141            if(pt){
14142                var el = n.ddel;
14143                var cls;
14144                if(pt == "above"){
14145                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
14146                    cls = "x-tree-drag-insert-above";
14147                }else if(pt == "below"){
14148                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
14149                    cls = "x-tree-drag-insert-below";
14150                }else{
14151                    returnCls = "x-tree-drop-ok-append";
14152                    cls = "x-tree-drag-append";
14153                }
14154                if(this.lastInsertClass != cls){
14155                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
14156                    this.lastInsertClass = cls;
14157                }
14158            }
14159        }
14160        return returnCls;
14161     },
14162     
14163     onNodeOut : function(n, dd, e, data){
14164         
14165         this.cancelExpand();
14166         this.removeDropIndicators(n);
14167     },
14168     
14169     onNodeDrop : function(n, dd, e, data){
14170         var point = this.getDropPoint(e, n, dd);
14171         var targetNode = n.node;
14172         targetNode.ui.startDrop();
14173         if(!this.isValidDropPoint(n, point, dd, e, data)){
14174             targetNode.ui.endDrop();
14175             return false;
14176         }
14177         // first try to find the drop node
14178         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
14179         var dropEvent = {
14180             tree : this.tree,
14181             target: targetNode,
14182             data: data,
14183             point: point,
14184             source: dd,
14185             rawEvent: e,
14186             dropNode: dropNode,
14187             cancel: !dropNode   
14188         };
14189         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
14190         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
14191             targetNode.ui.endDrop();
14192             return false;
14193         }
14194         // allow target changing
14195         targetNode = dropEvent.target;
14196         if(point == "append" && !targetNode.isExpanded()){
14197             targetNode.expand(false, null, function(){
14198                 this.completeDrop(dropEvent);
14199             }.createDelegate(this));
14200         }else{
14201             this.completeDrop(dropEvent);
14202         }
14203         return true;
14204     },
14205     
14206     completeDrop : function(de){
14207         var ns = de.dropNode, p = de.point, t = de.target;
14208         if(!(ns instanceof Array)){
14209             ns = [ns];
14210         }
14211         var n;
14212         for(var i = 0, len = ns.length; i < len; i++){
14213             n = ns[i];
14214             if(p == "above"){
14215                 t.parentNode.insertBefore(n, t);
14216             }else if(p == "below"){
14217                 t.parentNode.insertBefore(n, t.nextSibling);
14218             }else{
14219                 t.appendChild(n);
14220             }
14221         }
14222         n.ui.focus();
14223         if(this.tree.hlDrop){
14224             n.ui.highlight();
14225         }
14226         t.ui.endDrop();
14227         this.tree.fireEvent("nodedrop", de);
14228     },
14229     
14230     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
14231         if(this.tree.hlDrop){
14232             dropNode.ui.focus();
14233             dropNode.ui.highlight();
14234         }
14235         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
14236     },
14237     
14238     getTree : function(){
14239         return this.tree;
14240     },
14241     
14242     removeDropIndicators : function(n){
14243         if(n && n.ddel){
14244             var el = n.ddel;
14245             Roo.fly(el).removeClass([
14246                     "x-tree-drag-insert-above",
14247                     "x-tree-drag-insert-below",
14248                     "x-tree-drag-append"]);
14249             this.lastInsertClass = "_noclass";
14250         }
14251     },
14252     
14253     beforeDragDrop : function(target, e, id){
14254         this.cancelExpand();
14255         return true;
14256     },
14257     
14258     afterRepair : function(data){
14259         if(data && Roo.enableFx){
14260             data.node.ui.highlight();
14261         }
14262         this.hideProxy();
14263     } 
14264     
14265 });
14266
14267 }
14268 /*
14269  * Based on:
14270  * Ext JS Library 1.1.1
14271  * Copyright(c) 2006-2007, Ext JS, LLC.
14272  *
14273  * Originally Released Under LGPL - original licence link has changed is not relivant.
14274  *
14275  * Fork - LGPL
14276  * <script type="text/javascript">
14277  */
14278  
14279
14280 if(Roo.dd.DragZone){
14281 Roo.tree.TreeDragZone = function(tree, config){
14282     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
14283     this.tree = tree;
14284 };
14285
14286 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
14287     ddGroup : "TreeDD",
14288    
14289     onBeforeDrag : function(data, e){
14290         var n = data.node;
14291         return n && n.draggable && !n.disabled;
14292     },
14293      
14294     
14295     onInitDrag : function(e){
14296         var data = this.dragData;
14297         this.tree.getSelectionModel().select(data.node);
14298         this.proxy.update("");
14299         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
14300         this.tree.fireEvent("startdrag", this.tree, data.node, e);
14301     },
14302     
14303     getRepairXY : function(e, data){
14304         return data.node.ui.getDDRepairXY();
14305     },
14306     
14307     onEndDrag : function(data, e){
14308         this.tree.fireEvent("enddrag", this.tree, data.node, e);
14309         
14310         
14311     },
14312     
14313     onValidDrop : function(dd, e, id){
14314         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
14315         this.hideProxy();
14316     },
14317     
14318     beforeInvalidDrop : function(e, id){
14319         // this scrolls the original position back into view
14320         var sm = this.tree.getSelectionModel();
14321         sm.clearSelections();
14322         sm.select(this.dragData.node);
14323     }
14324 });
14325 }/*
14326  * Based on:
14327  * Ext JS Library 1.1.1
14328  * Copyright(c) 2006-2007, Ext JS, LLC.
14329  *
14330  * Originally Released Under LGPL - original licence link has changed is not relivant.
14331  *
14332  * Fork - LGPL
14333  * <script type="text/javascript">
14334  */
14335 /**
14336  * @class Roo.tree.TreeEditor
14337  * @extends Roo.Editor
14338  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
14339  * as the editor field.
14340  * @constructor
14341  * @param {Object} config (used to be the tree panel.)
14342  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
14343  * 
14344  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
14345  * @cfg {Roo.form.TextField|Object} field The field configuration
14346  *
14347  * 
14348  */
14349 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
14350     var tree = config;
14351     var field;
14352     if (oldconfig) { // old style..
14353         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14354     } else {
14355         // new style..
14356         tree = config.tree;
14357         config.field = config.field  || {};
14358         config.field.xtype = 'TextField';
14359         field = Roo.factory(config.field, Roo.form);
14360     }
14361     config = config || {};
14362     
14363     
14364     this.addEvents({
14365         /**
14366          * @event beforenodeedit
14367          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14368          * false from the handler of this event.
14369          * @param {Editor} this
14370          * @param {Roo.tree.Node} node 
14371          */
14372         "beforenodeedit" : true
14373     });
14374     
14375     //Roo.log(config);
14376     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14377
14378     this.tree = tree;
14379
14380     tree.on('beforeclick', this.beforeNodeClick, this);
14381     tree.getTreeEl().on('mousedown', this.hide, this);
14382     this.on('complete', this.updateNode, this);
14383     this.on('beforestartedit', this.fitToTree, this);
14384     this.on('startedit', this.bindScroll, this, {delay:10});
14385     this.on('specialkey', this.onSpecialKey, this);
14386 };
14387
14388 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14389     /**
14390      * @cfg {String} alignment
14391      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14392      */
14393     alignment: "l-l",
14394     // inherit
14395     autoSize: false,
14396     /**
14397      * @cfg {Boolean} hideEl
14398      * True to hide the bound element while the editor is displayed (defaults to false)
14399      */
14400     hideEl : false,
14401     /**
14402      * @cfg {String} cls
14403      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14404      */
14405     cls: "x-small-editor x-tree-editor",
14406     /**
14407      * @cfg {Boolean} shim
14408      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14409      */
14410     shim:false,
14411     // inherit
14412     shadow:"frame",
14413     /**
14414      * @cfg {Number} maxWidth
14415      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14416      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14417      * scroll and client offsets into account prior to each edit.
14418      */
14419     maxWidth: 250,
14420
14421     editDelay : 350,
14422
14423     // private
14424     fitToTree : function(ed, el){
14425         var td = this.tree.getTreeEl().dom, nd = el.dom;
14426         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14427             td.scrollLeft = nd.offsetLeft;
14428         }
14429         var w = Math.min(
14430                 this.maxWidth,
14431                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14432         this.setSize(w, '');
14433         
14434         return this.fireEvent('beforenodeedit', this, this.editNode);
14435         
14436     },
14437
14438     // private
14439     triggerEdit : function(node){
14440         this.completeEdit();
14441         this.editNode = node;
14442         this.startEdit(node.ui.textNode, node.text);
14443     },
14444
14445     // private
14446     bindScroll : function(){
14447         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14448     },
14449
14450     // private
14451     beforeNodeClick : function(node, e){
14452         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14453         this.lastClick = new Date();
14454         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14455             e.stopEvent();
14456             this.triggerEdit(node);
14457             return false;
14458         }
14459         return true;
14460     },
14461
14462     // private
14463     updateNode : function(ed, value){
14464         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14465         this.editNode.setText(value);
14466     },
14467
14468     // private
14469     onHide : function(){
14470         Roo.tree.TreeEditor.superclass.onHide.call(this);
14471         if(this.editNode){
14472             this.editNode.ui.focus();
14473         }
14474     },
14475
14476     // private
14477     onSpecialKey : function(field, e){
14478         var k = e.getKey();
14479         if(k == e.ESC){
14480             e.stopEvent();
14481             this.cancelEdit();
14482         }else if(k == e.ENTER && !e.hasModifier()){
14483             e.stopEvent();
14484             this.completeEdit();
14485         }
14486     }
14487 });//<Script type="text/javascript">
14488 /*
14489  * Based on:
14490  * Ext JS Library 1.1.1
14491  * Copyright(c) 2006-2007, Ext JS, LLC.
14492  *
14493  * Originally Released Under LGPL - original licence link has changed is not relivant.
14494  *
14495  * Fork - LGPL
14496  * <script type="text/javascript">
14497  */
14498  
14499 /**
14500  * Not documented??? - probably should be...
14501  */
14502
14503 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14504     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14505     
14506     renderElements : function(n, a, targetNode, bulkRender){
14507         //consel.log("renderElements?");
14508         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14509
14510         var t = n.getOwnerTree();
14511         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14512         
14513         var cols = t.columns;
14514         var bw = t.borderWidth;
14515         var c = cols[0];
14516         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14517          var cb = typeof a.checked == "boolean";
14518         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14519         var colcls = 'x-t-' + tid + '-c0';
14520         var buf = [
14521             '<li class="x-tree-node">',
14522             
14523                 
14524                 '<div class="x-tree-node-el ', a.cls,'">',
14525                     // extran...
14526                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14527                 
14528                 
14529                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14530                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14531                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14532                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14533                            (a.iconCls ? ' '+a.iconCls : ''),
14534                            '" unselectable="on" />',
14535                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14536                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14537                              
14538                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14539                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14540                             '<span unselectable="on" qtip="' + tx + '">',
14541                              tx,
14542                              '</span></a>' ,
14543                     '</div>',
14544                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14545                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14546                  ];
14547         for(var i = 1, len = cols.length; i < len; i++){
14548             c = cols[i];
14549             colcls = 'x-t-' + tid + '-c' +i;
14550             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14551             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14552                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14553                       "</div>");
14554          }
14555          
14556          buf.push(
14557             '</a>',
14558             '<div class="x-clear"></div></div>',
14559             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14560             "</li>");
14561         
14562         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14563             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14564                                 n.nextSibling.ui.getEl(), buf.join(""));
14565         }else{
14566             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14567         }
14568         var el = this.wrap.firstChild;
14569         this.elRow = el;
14570         this.elNode = el.firstChild;
14571         this.ranchor = el.childNodes[1];
14572         this.ctNode = this.wrap.childNodes[1];
14573         var cs = el.firstChild.childNodes;
14574         this.indentNode = cs[0];
14575         this.ecNode = cs[1];
14576         this.iconNode = cs[2];
14577         var index = 3;
14578         if(cb){
14579             this.checkbox = cs[3];
14580             index++;
14581         }
14582         this.anchor = cs[index];
14583         
14584         this.textNode = cs[index].firstChild;
14585         
14586         //el.on("click", this.onClick, this);
14587         //el.on("dblclick", this.onDblClick, this);
14588         
14589         
14590        // console.log(this);
14591     },
14592     initEvents : function(){
14593         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14594         
14595             
14596         var a = this.ranchor;
14597
14598         var el = Roo.get(a);
14599
14600         if(Roo.isOpera){ // opera render bug ignores the CSS
14601             el.setStyle("text-decoration", "none");
14602         }
14603
14604         el.on("click", this.onClick, this);
14605         el.on("dblclick", this.onDblClick, this);
14606         el.on("contextmenu", this.onContextMenu, this);
14607         
14608     },
14609     
14610     /*onSelectedChange : function(state){
14611         if(state){
14612             this.focus();
14613             this.addClass("x-tree-selected");
14614         }else{
14615             //this.blur();
14616             this.removeClass("x-tree-selected");
14617         }
14618     },*/
14619     addClass : function(cls){
14620         if(this.elRow){
14621             Roo.fly(this.elRow).addClass(cls);
14622         }
14623         
14624     },
14625     
14626     
14627     removeClass : function(cls){
14628         if(this.elRow){
14629             Roo.fly(this.elRow).removeClass(cls);
14630         }
14631     }
14632
14633     
14634     
14635 });//<Script type="text/javascript">
14636
14637 /*
14638  * Based on:
14639  * Ext JS Library 1.1.1
14640  * Copyright(c) 2006-2007, Ext JS, LLC.
14641  *
14642  * Originally Released Under LGPL - original licence link has changed is not relivant.
14643  *
14644  * Fork - LGPL
14645  * <script type="text/javascript">
14646  */
14647  
14648
14649 /**
14650  * @class Roo.tree.ColumnTree
14651  * @extends Roo.data.TreePanel
14652  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14653  * @cfg {int} borderWidth  compined right/left border allowance
14654  * @constructor
14655  * @param {String/HTMLElement/Element} el The container element
14656  * @param {Object} config
14657  */
14658 Roo.tree.ColumnTree =  function(el, config)
14659 {
14660    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14661    this.addEvents({
14662         /**
14663         * @event resize
14664         * Fire this event on a container when it resizes
14665         * @param {int} w Width
14666         * @param {int} h Height
14667         */
14668        "resize" : true
14669     });
14670     this.on('resize', this.onResize, this);
14671 };
14672
14673 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14674     //lines:false,
14675     
14676     
14677     borderWidth: Roo.isBorderBox ? 0 : 2, 
14678     headEls : false,
14679     
14680     render : function(){
14681         // add the header.....
14682        
14683         Roo.tree.ColumnTree.superclass.render.apply(this);
14684         
14685         this.el.addClass('x-column-tree');
14686         
14687         this.headers = this.el.createChild(
14688             {cls:'x-tree-headers'},this.innerCt.dom);
14689    
14690         var cols = this.columns, c;
14691         var totalWidth = 0;
14692         this.headEls = [];
14693         var  len = cols.length;
14694         for(var i = 0; i < len; i++){
14695              c = cols[i];
14696              totalWidth += c.width;
14697             this.headEls.push(this.headers.createChild({
14698                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14699                  cn: {
14700                      cls:'x-tree-hd-text',
14701                      html: c.header
14702                  },
14703                  style:'width:'+(c.width-this.borderWidth)+'px;'
14704              }));
14705         }
14706         this.headers.createChild({cls:'x-clear'});
14707         // prevent floats from wrapping when clipped
14708         this.headers.setWidth(totalWidth);
14709         //this.innerCt.setWidth(totalWidth);
14710         this.innerCt.setStyle({ overflow: 'auto' });
14711         this.onResize(this.width, this.height);
14712              
14713         
14714     },
14715     onResize : function(w,h)
14716     {
14717         this.height = h;
14718         this.width = w;
14719         // resize cols..
14720         this.innerCt.setWidth(this.width);
14721         this.innerCt.setHeight(this.height-20);
14722         
14723         // headers...
14724         var cols = this.columns, c;
14725         var totalWidth = 0;
14726         var expEl = false;
14727         var len = cols.length;
14728         for(var i = 0; i < len; i++){
14729             c = cols[i];
14730             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14731                 // it's the expander..
14732                 expEl  = this.headEls[i];
14733                 continue;
14734             }
14735             totalWidth += c.width;
14736             
14737         }
14738         if (expEl) {
14739             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14740         }
14741         this.headers.setWidth(w-20);
14742
14743         
14744         
14745         
14746     }
14747 });
14748 /*
14749  * Based on:
14750  * Ext JS Library 1.1.1
14751  * Copyright(c) 2006-2007, Ext JS, LLC.
14752  *
14753  * Originally Released Under LGPL - original licence link has changed is not relivant.
14754  *
14755  * Fork - LGPL
14756  * <script type="text/javascript">
14757  */
14758  
14759 /**
14760  * @class Roo.menu.Menu
14761  * @extends Roo.util.Observable
14762  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14763  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14764  * @constructor
14765  * Creates a new Menu
14766  * @param {Object} config Configuration options
14767  */
14768 Roo.menu.Menu = function(config){
14769     Roo.apply(this, config);
14770     this.id = this.id || Roo.id();
14771     this.addEvents({
14772         /**
14773          * @event beforeshow
14774          * Fires before this menu is displayed
14775          * @param {Roo.menu.Menu} this
14776          */
14777         beforeshow : true,
14778         /**
14779          * @event beforehide
14780          * Fires before this menu is hidden
14781          * @param {Roo.menu.Menu} this
14782          */
14783         beforehide : true,
14784         /**
14785          * @event show
14786          * Fires after this menu is displayed
14787          * @param {Roo.menu.Menu} this
14788          */
14789         show : true,
14790         /**
14791          * @event hide
14792          * Fires after this menu is hidden
14793          * @param {Roo.menu.Menu} this
14794          */
14795         hide : true,
14796         /**
14797          * @event click
14798          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14799          * @param {Roo.menu.Menu} this
14800          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14801          * @param {Roo.EventObject} e
14802          */
14803         click : true,
14804         /**
14805          * @event mouseover
14806          * Fires when the mouse is hovering over this menu
14807          * @param {Roo.menu.Menu} this
14808          * @param {Roo.EventObject} e
14809          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14810          */
14811         mouseover : true,
14812         /**
14813          * @event mouseout
14814          * Fires when the mouse exits this menu
14815          * @param {Roo.menu.Menu} this
14816          * @param {Roo.EventObject} e
14817          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14818          */
14819         mouseout : true,
14820         /**
14821          * @event itemclick
14822          * Fires when a menu item contained in this menu is clicked
14823          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14824          * @param {Roo.EventObject} e
14825          */
14826         itemclick: true
14827     });
14828     if (this.registerMenu) {
14829         Roo.menu.MenuMgr.register(this);
14830     }
14831     
14832     var mis = this.items;
14833     this.items = new Roo.util.MixedCollection();
14834     if(mis){
14835         this.add.apply(this, mis);
14836     }
14837 };
14838
14839 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14840     /**
14841      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14842      */
14843     minWidth : 120,
14844     /**
14845      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14846      * for bottom-right shadow (defaults to "sides")
14847      */
14848     shadow : "sides",
14849     /**
14850      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14851      * this menu (defaults to "tl-tr?")
14852      */
14853     subMenuAlign : "tl-tr?",
14854     /**
14855      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14856      * relative to its element of origin (defaults to "tl-bl?")
14857      */
14858     defaultAlign : "tl-bl?",
14859     /**
14860      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14861      */
14862     allowOtherMenus : false,
14863     /**
14864      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14865      */
14866     registerMenu : true,
14867
14868     hidden:true,
14869
14870     // private
14871     render : function(){
14872         if(this.el){
14873             return;
14874         }
14875         var el = this.el = new Roo.Layer({
14876             cls: "x-menu",
14877             shadow:this.shadow,
14878             constrain: false,
14879             parentEl: this.parentEl || document.body,
14880             zindex:15000
14881         });
14882
14883         this.keyNav = new Roo.menu.MenuNav(this);
14884
14885         if(this.plain){
14886             el.addClass("x-menu-plain");
14887         }
14888         if(this.cls){
14889             el.addClass(this.cls);
14890         }
14891         // generic focus element
14892         this.focusEl = el.createChild({
14893             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14894         });
14895         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14896         //disabling touch- as it's causing issues ..
14897         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14898         ul.on('click'   , this.onClick, this);
14899         
14900         
14901         ul.on("mouseover", this.onMouseOver, this);
14902         ul.on("mouseout", this.onMouseOut, this);
14903         this.items.each(function(item){
14904             if (item.hidden) {
14905                 return;
14906             }
14907             
14908             var li = document.createElement("li");
14909             li.className = "x-menu-list-item";
14910             ul.dom.appendChild(li);
14911             item.render(li, this);
14912         }, this);
14913         this.ul = ul;
14914         this.autoWidth();
14915     },
14916
14917     // private
14918     autoWidth : function(){
14919         var el = this.el, ul = this.ul;
14920         if(!el){
14921             return;
14922         }
14923         var w = this.width;
14924         if(w){
14925             el.setWidth(w);
14926         }else if(Roo.isIE){
14927             el.setWidth(this.minWidth);
14928             var t = el.dom.offsetWidth; // force recalc
14929             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14930         }
14931     },
14932
14933     // private
14934     delayAutoWidth : function(){
14935         if(this.rendered){
14936             if(!this.awTask){
14937                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14938             }
14939             this.awTask.delay(20);
14940         }
14941     },
14942
14943     // private
14944     findTargetItem : function(e){
14945         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14946         if(t && t.menuItemId){
14947             return this.items.get(t.menuItemId);
14948         }
14949     },
14950
14951     // private
14952     onClick : function(e){
14953         Roo.log("menu.onClick");
14954         var t = this.findTargetItem(e);
14955         if(!t){
14956             return;
14957         }
14958         Roo.log(e);
14959         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14960             if(t == this.activeItem && t.shouldDeactivate(e)){
14961                 this.activeItem.deactivate();
14962                 delete this.activeItem;
14963                 return;
14964             }
14965             if(t.canActivate){
14966                 this.setActiveItem(t, true);
14967             }
14968             return;
14969             
14970             
14971         }
14972         
14973         t.onClick(e);
14974         this.fireEvent("click", this, t, e);
14975     },
14976
14977     // private
14978     setActiveItem : function(item, autoExpand){
14979         if(item != this.activeItem){
14980             if(this.activeItem){
14981                 this.activeItem.deactivate();
14982             }
14983             this.activeItem = item;
14984             item.activate(autoExpand);
14985         }else if(autoExpand){
14986             item.expandMenu();
14987         }
14988     },
14989
14990     // private
14991     tryActivate : function(start, step){
14992         var items = this.items;
14993         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
14994             var item = items.get(i);
14995             if(!item.disabled && item.canActivate){
14996                 this.setActiveItem(item, false);
14997                 return item;
14998             }
14999         }
15000         return false;
15001     },
15002
15003     // private
15004     onMouseOver : function(e){
15005         var t;
15006         if(t = this.findTargetItem(e)){
15007             if(t.canActivate && !t.disabled){
15008                 this.setActiveItem(t, true);
15009             }
15010         }
15011         this.fireEvent("mouseover", this, e, t);
15012     },
15013
15014     // private
15015     onMouseOut : function(e){
15016         var t;
15017         if(t = this.findTargetItem(e)){
15018             if(t == this.activeItem && t.shouldDeactivate(e)){
15019                 this.activeItem.deactivate();
15020                 delete this.activeItem;
15021             }
15022         }
15023         this.fireEvent("mouseout", this, e, t);
15024     },
15025
15026     /**
15027      * Read-only.  Returns true if the menu is currently displayed, else false.
15028      * @type Boolean
15029      */
15030     isVisible : function(){
15031         return this.el && !this.hidden;
15032     },
15033
15034     /**
15035      * Displays this menu relative to another element
15036      * @param {String/HTMLElement/Roo.Element} element The element to align to
15037      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
15038      * the element (defaults to this.defaultAlign)
15039      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15040      */
15041     show : function(el, pos, parentMenu){
15042         this.parentMenu = parentMenu;
15043         if(!this.el){
15044             this.render();
15045         }
15046         this.fireEvent("beforeshow", this);
15047         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
15048     },
15049
15050     /**
15051      * Displays this menu at a specific xy position
15052      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
15053      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15054      */
15055     showAt : function(xy, parentMenu, /* private: */_e){
15056         this.parentMenu = parentMenu;
15057         if(!this.el){
15058             this.render();
15059         }
15060         if(_e !== false){
15061             this.fireEvent("beforeshow", this);
15062             xy = this.el.adjustForConstraints(xy);
15063         }
15064         this.el.setXY(xy);
15065         this.el.show();
15066         this.hidden = false;
15067         this.focus();
15068         this.fireEvent("show", this);
15069     },
15070
15071     focus : function(){
15072         if(!this.hidden){
15073             this.doFocus.defer(50, this);
15074         }
15075     },
15076
15077     doFocus : function(){
15078         if(!this.hidden){
15079             this.focusEl.focus();
15080         }
15081     },
15082
15083     /**
15084      * Hides this menu and optionally all parent menus
15085      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
15086      */
15087     hide : function(deep){
15088         if(this.el && this.isVisible()){
15089             this.fireEvent("beforehide", this);
15090             if(this.activeItem){
15091                 this.activeItem.deactivate();
15092                 this.activeItem = null;
15093             }
15094             this.el.hide();
15095             this.hidden = true;
15096             this.fireEvent("hide", this);
15097         }
15098         if(deep === true && this.parentMenu){
15099             this.parentMenu.hide(true);
15100         }
15101     },
15102
15103     /**
15104      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
15105      * Any of the following are valid:
15106      * <ul>
15107      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
15108      * <li>An HTMLElement object which will be converted to a menu item</li>
15109      * <li>A menu item config object that will be created as a new menu item</li>
15110      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
15111      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
15112      * </ul>
15113      * Usage:
15114      * <pre><code>
15115 // Create the menu
15116 var menu = new Roo.menu.Menu();
15117
15118 // Create a menu item to add by reference
15119 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
15120
15121 // Add a bunch of items at once using different methods.
15122 // Only the last item added will be returned.
15123 var item = menu.add(
15124     menuItem,                // add existing item by ref
15125     'Dynamic Item',          // new TextItem
15126     '-',                     // new separator
15127     { text: 'Config Item' }  // new item by config
15128 );
15129 </code></pre>
15130      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
15131      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
15132      */
15133     add : function(){
15134         var a = arguments, l = a.length, item;
15135         for(var i = 0; i < l; i++){
15136             var el = a[i];
15137             if ((typeof(el) == "object") && el.xtype && el.xns) {
15138                 el = Roo.factory(el, Roo.menu);
15139             }
15140             
15141             if(el.render){ // some kind of Item
15142                 item = this.addItem(el);
15143             }else if(typeof el == "string"){ // string
15144                 if(el == "separator" || el == "-"){
15145                     item = this.addSeparator();
15146                 }else{
15147                     item = this.addText(el);
15148                 }
15149             }else if(el.tagName || el.el){ // element
15150                 item = this.addElement(el);
15151             }else if(typeof el == "object"){ // must be menu item config?
15152                 item = this.addMenuItem(el);
15153             }
15154         }
15155         return item;
15156     },
15157
15158     /**
15159      * Returns this menu's underlying {@link Roo.Element} object
15160      * @return {Roo.Element} The element
15161      */
15162     getEl : function(){
15163         if(!this.el){
15164             this.render();
15165         }
15166         return this.el;
15167     },
15168
15169     /**
15170      * Adds a separator bar to the menu
15171      * @return {Roo.menu.Item} The menu item that was added
15172      */
15173     addSeparator : function(){
15174         return this.addItem(new Roo.menu.Separator());
15175     },
15176
15177     /**
15178      * Adds an {@link Roo.Element} object to the menu
15179      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
15180      * @return {Roo.menu.Item} The menu item that was added
15181      */
15182     addElement : function(el){
15183         return this.addItem(new Roo.menu.BaseItem(el));
15184     },
15185
15186     /**
15187      * Adds an existing object based on {@link Roo.menu.Item} to the menu
15188      * @param {Roo.menu.Item} item The menu item to add
15189      * @return {Roo.menu.Item} The menu item that was added
15190      */
15191     addItem : function(item){
15192         this.items.add(item);
15193         if(this.ul){
15194             var li = document.createElement("li");
15195             li.className = "x-menu-list-item";
15196             this.ul.dom.appendChild(li);
15197             item.render(li, this);
15198             this.delayAutoWidth();
15199         }
15200         return item;
15201     },
15202
15203     /**
15204      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
15205      * @param {Object} config A MenuItem config object
15206      * @return {Roo.menu.Item} The menu item that was added
15207      */
15208     addMenuItem : function(config){
15209         if(!(config instanceof Roo.menu.Item)){
15210             if(typeof config.checked == "boolean"){ // must be check menu item config?
15211                 config = new Roo.menu.CheckItem(config);
15212             }else{
15213                 config = new Roo.menu.Item(config);
15214             }
15215         }
15216         return this.addItem(config);
15217     },
15218
15219     /**
15220      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
15221      * @param {String} text The text to display in the menu item
15222      * @return {Roo.menu.Item} The menu item that was added
15223      */
15224     addText : function(text){
15225         return this.addItem(new Roo.menu.TextItem({ text : text }));
15226     },
15227
15228     /**
15229      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
15230      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
15231      * @param {Roo.menu.Item} item The menu item to add
15232      * @return {Roo.menu.Item} The menu item that was added
15233      */
15234     insert : function(index, item){
15235         this.items.insert(index, item);
15236         if(this.ul){
15237             var li = document.createElement("li");
15238             li.className = "x-menu-list-item";
15239             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
15240             item.render(li, this);
15241             this.delayAutoWidth();
15242         }
15243         return item;
15244     },
15245
15246     /**
15247      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
15248      * @param {Roo.menu.Item} item The menu item to remove
15249      */
15250     remove : function(item){
15251         this.items.removeKey(item.id);
15252         item.destroy();
15253     },
15254
15255     /**
15256      * Removes and destroys all items in the menu
15257      */
15258     removeAll : function(){
15259         var f;
15260         while(f = this.items.first()){
15261             this.remove(f);
15262         }
15263     }
15264 });
15265
15266 // MenuNav is a private utility class used internally by the Menu
15267 Roo.menu.MenuNav = function(menu){
15268     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
15269     this.scope = this.menu = menu;
15270 };
15271
15272 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
15273     doRelay : function(e, h){
15274         var k = e.getKey();
15275         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
15276             this.menu.tryActivate(0, 1);
15277             return false;
15278         }
15279         return h.call(this.scope || this, e, this.menu);
15280     },
15281
15282     up : function(e, m){
15283         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
15284             m.tryActivate(m.items.length-1, -1);
15285         }
15286     },
15287
15288     down : function(e, m){
15289         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
15290             m.tryActivate(0, 1);
15291         }
15292     },
15293
15294     right : function(e, m){
15295         if(m.activeItem){
15296             m.activeItem.expandMenu(true);
15297         }
15298     },
15299
15300     left : function(e, m){
15301         m.hide();
15302         if(m.parentMenu && m.parentMenu.activeItem){
15303             m.parentMenu.activeItem.activate();
15304         }
15305     },
15306
15307     enter : function(e, m){
15308         if(m.activeItem){
15309             e.stopPropagation();
15310             m.activeItem.onClick(e);
15311             m.fireEvent("click", this, m.activeItem);
15312             return true;
15313         }
15314     }
15315 });/*
15316  * Based on:
15317  * Ext JS Library 1.1.1
15318  * Copyright(c) 2006-2007, Ext JS, LLC.
15319  *
15320  * Originally Released Under LGPL - original licence link has changed is not relivant.
15321  *
15322  * Fork - LGPL
15323  * <script type="text/javascript">
15324  */
15325  
15326 /**
15327  * @class Roo.menu.MenuMgr
15328  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
15329  * @singleton
15330  */
15331 Roo.menu.MenuMgr = function(){
15332    var menus, active, groups = {}, attached = false, lastShow = new Date();
15333
15334    // private - called when first menu is created
15335    function init(){
15336        menus = {};
15337        active = new Roo.util.MixedCollection();
15338        Roo.get(document).addKeyListener(27, function(){
15339            if(active.length > 0){
15340                hideAll();
15341            }
15342        });
15343    }
15344
15345    // private
15346    function hideAll(){
15347        if(active && active.length > 0){
15348            var c = active.clone();
15349            c.each(function(m){
15350                m.hide();
15351            });
15352        }
15353    }
15354
15355    // private
15356    function onHide(m){
15357        active.remove(m);
15358        if(active.length < 1){
15359            Roo.get(document).un("mousedown", onMouseDown);
15360            attached = false;
15361        }
15362    }
15363
15364    // private
15365    function onShow(m){
15366        var last = active.last();
15367        lastShow = new Date();
15368        active.add(m);
15369        if(!attached){
15370            Roo.get(document).on("mousedown", onMouseDown);
15371            attached = true;
15372        }
15373        if(m.parentMenu){
15374           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15375           m.parentMenu.activeChild = m;
15376        }else if(last && last.isVisible()){
15377           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15378        }
15379    }
15380
15381    // private
15382    function onBeforeHide(m){
15383        if(m.activeChild){
15384            m.activeChild.hide();
15385        }
15386        if(m.autoHideTimer){
15387            clearTimeout(m.autoHideTimer);
15388            delete m.autoHideTimer;
15389        }
15390    }
15391
15392    // private
15393    function onBeforeShow(m){
15394        var pm = m.parentMenu;
15395        if(!pm && !m.allowOtherMenus){
15396            hideAll();
15397        }else if(pm && pm.activeChild && active != m){
15398            pm.activeChild.hide();
15399        }
15400    }
15401
15402    // private
15403    function onMouseDown(e){
15404        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15405            hideAll();
15406        }
15407    }
15408
15409    // private
15410    function onBeforeCheck(mi, state){
15411        if(state){
15412            var g = groups[mi.group];
15413            for(var i = 0, l = g.length; i < l; i++){
15414                if(g[i] != mi){
15415                    g[i].setChecked(false);
15416                }
15417            }
15418        }
15419    }
15420
15421    return {
15422
15423        /**
15424         * Hides all menus that are currently visible
15425         */
15426        hideAll : function(){
15427             hideAll();  
15428        },
15429
15430        // private
15431        register : function(menu){
15432            if(!menus){
15433                init();
15434            }
15435            menus[menu.id] = menu;
15436            menu.on("beforehide", onBeforeHide);
15437            menu.on("hide", onHide);
15438            menu.on("beforeshow", onBeforeShow);
15439            menu.on("show", onShow);
15440            var g = menu.group;
15441            if(g && menu.events["checkchange"]){
15442                if(!groups[g]){
15443                    groups[g] = [];
15444                }
15445                groups[g].push(menu);
15446                menu.on("checkchange", onCheck);
15447            }
15448        },
15449
15450         /**
15451          * Returns a {@link Roo.menu.Menu} object
15452          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15453          * be used to generate and return a new Menu instance.
15454          */
15455        get : function(menu){
15456            if(typeof menu == "string"){ // menu id
15457                return menus[menu];
15458            }else if(menu.events){  // menu instance
15459                return menu;
15460            }else if(typeof menu.length == 'number'){ // array of menu items?
15461                return new Roo.menu.Menu({items:menu});
15462            }else{ // otherwise, must be a config
15463                return new Roo.menu.Menu(menu);
15464            }
15465        },
15466
15467        // private
15468        unregister : function(menu){
15469            delete menus[menu.id];
15470            menu.un("beforehide", onBeforeHide);
15471            menu.un("hide", onHide);
15472            menu.un("beforeshow", onBeforeShow);
15473            menu.un("show", onShow);
15474            var g = menu.group;
15475            if(g && menu.events["checkchange"]){
15476                groups[g].remove(menu);
15477                menu.un("checkchange", onCheck);
15478            }
15479        },
15480
15481        // private
15482        registerCheckable : function(menuItem){
15483            var g = menuItem.group;
15484            if(g){
15485                if(!groups[g]){
15486                    groups[g] = [];
15487                }
15488                groups[g].push(menuItem);
15489                menuItem.on("beforecheckchange", onBeforeCheck);
15490            }
15491        },
15492
15493        // private
15494        unregisterCheckable : function(menuItem){
15495            var g = menuItem.group;
15496            if(g){
15497                groups[g].remove(menuItem);
15498                menuItem.un("beforecheckchange", onBeforeCheck);
15499            }
15500        }
15501    };
15502 }();/*
15503  * Based on:
15504  * Ext JS Library 1.1.1
15505  * Copyright(c) 2006-2007, Ext JS, LLC.
15506  *
15507  * Originally Released Under LGPL - original licence link has changed is not relivant.
15508  *
15509  * Fork - LGPL
15510  * <script type="text/javascript">
15511  */
15512  
15513
15514 /**
15515  * @class Roo.menu.BaseItem
15516  * @extends Roo.Component
15517  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15518  * management and base configuration options shared by all menu components.
15519  * @constructor
15520  * Creates a new BaseItem
15521  * @param {Object} config Configuration options
15522  */
15523 Roo.menu.BaseItem = function(config){
15524     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15525
15526     this.addEvents({
15527         /**
15528          * @event click
15529          * Fires when this item is clicked
15530          * @param {Roo.menu.BaseItem} this
15531          * @param {Roo.EventObject} e
15532          */
15533         click: true,
15534         /**
15535          * @event activate
15536          * Fires when this item is activated
15537          * @param {Roo.menu.BaseItem} this
15538          */
15539         activate : true,
15540         /**
15541          * @event deactivate
15542          * Fires when this item is deactivated
15543          * @param {Roo.menu.BaseItem} this
15544          */
15545         deactivate : true
15546     });
15547
15548     if(this.handler){
15549         this.on("click", this.handler, this.scope, true);
15550     }
15551 };
15552
15553 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15554     /**
15555      * @cfg {Function} handler
15556      * A function that will handle the click event of this menu item (defaults to undefined)
15557      */
15558     /**
15559      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15560      */
15561     canActivate : false,
15562     
15563      /**
15564      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15565      */
15566     hidden: false,
15567     
15568     /**
15569      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15570      */
15571     activeClass : "x-menu-item-active",
15572     /**
15573      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15574      */
15575     hideOnClick : true,
15576     /**
15577      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15578      */
15579     hideDelay : 100,
15580
15581     // private
15582     ctype: "Roo.menu.BaseItem",
15583
15584     // private
15585     actionMode : "container",
15586
15587     // private
15588     render : function(container, parentMenu){
15589         this.parentMenu = parentMenu;
15590         Roo.menu.BaseItem.superclass.render.call(this, container);
15591         this.container.menuItemId = this.id;
15592     },
15593
15594     // private
15595     onRender : function(container, position){
15596         this.el = Roo.get(this.el);
15597         container.dom.appendChild(this.el.dom);
15598     },
15599
15600     // private
15601     onClick : function(e){
15602         if(!this.disabled && this.fireEvent("click", this, e) !== false
15603                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15604             this.handleClick(e);
15605         }else{
15606             e.stopEvent();
15607         }
15608     },
15609
15610     // private
15611     activate : function(){
15612         if(this.disabled){
15613             return false;
15614         }
15615         var li = this.container;
15616         li.addClass(this.activeClass);
15617         this.region = li.getRegion().adjust(2, 2, -2, -2);
15618         this.fireEvent("activate", this);
15619         return true;
15620     },
15621
15622     // private
15623     deactivate : function(){
15624         this.container.removeClass(this.activeClass);
15625         this.fireEvent("deactivate", this);
15626     },
15627
15628     // private
15629     shouldDeactivate : function(e){
15630         return !this.region || !this.region.contains(e.getPoint());
15631     },
15632
15633     // private
15634     handleClick : function(e){
15635         if(this.hideOnClick){
15636             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15637         }
15638     },
15639
15640     // private
15641     expandMenu : function(autoActivate){
15642         // do nothing
15643     },
15644
15645     // private
15646     hideMenu : function(){
15647         // do nothing
15648     }
15649 });/*
15650  * Based on:
15651  * Ext JS Library 1.1.1
15652  * Copyright(c) 2006-2007, Ext JS, LLC.
15653  *
15654  * Originally Released Under LGPL - original licence link has changed is not relivant.
15655  *
15656  * Fork - LGPL
15657  * <script type="text/javascript">
15658  */
15659  
15660 /**
15661  * @class Roo.menu.Adapter
15662  * @extends Roo.menu.BaseItem
15663  * 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.
15664  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15665  * @constructor
15666  * Creates a new Adapter
15667  * @param {Object} config Configuration options
15668  */
15669 Roo.menu.Adapter = function(component, config){
15670     Roo.menu.Adapter.superclass.constructor.call(this, config);
15671     this.component = component;
15672 };
15673 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15674     // private
15675     canActivate : true,
15676
15677     // private
15678     onRender : function(container, position){
15679         this.component.render(container);
15680         this.el = this.component.getEl();
15681     },
15682
15683     // private
15684     activate : function(){
15685         if(this.disabled){
15686             return false;
15687         }
15688         this.component.focus();
15689         this.fireEvent("activate", this);
15690         return true;
15691     },
15692
15693     // private
15694     deactivate : function(){
15695         this.fireEvent("deactivate", this);
15696     },
15697
15698     // private
15699     disable : function(){
15700         this.component.disable();
15701         Roo.menu.Adapter.superclass.disable.call(this);
15702     },
15703
15704     // private
15705     enable : function(){
15706         this.component.enable();
15707         Roo.menu.Adapter.superclass.enable.call(this);
15708     }
15709 });/*
15710  * Based on:
15711  * Ext JS Library 1.1.1
15712  * Copyright(c) 2006-2007, Ext JS, LLC.
15713  *
15714  * Originally Released Under LGPL - original licence link has changed is not relivant.
15715  *
15716  * Fork - LGPL
15717  * <script type="text/javascript">
15718  */
15719
15720 /**
15721  * @class Roo.menu.TextItem
15722  * @extends Roo.menu.BaseItem
15723  * Adds a static text string to a menu, usually used as either a heading or group separator.
15724  * Note: old style constructor with text is still supported.
15725  * 
15726  * @constructor
15727  * Creates a new TextItem
15728  * @param {Object} cfg Configuration
15729  */
15730 Roo.menu.TextItem = function(cfg){
15731     if (typeof(cfg) == 'string') {
15732         this.text = cfg;
15733     } else {
15734         Roo.apply(this,cfg);
15735     }
15736     
15737     Roo.menu.TextItem.superclass.constructor.call(this);
15738 };
15739
15740 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15741     /**
15742      * @cfg {Boolean} text Text to show on item.
15743      */
15744     text : '',
15745     
15746     /**
15747      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15748      */
15749     hideOnClick : false,
15750     /**
15751      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15752      */
15753     itemCls : "x-menu-text",
15754
15755     // private
15756     onRender : function(){
15757         var s = document.createElement("span");
15758         s.className = this.itemCls;
15759         s.innerHTML = this.text;
15760         this.el = s;
15761         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15762     }
15763 });/*
15764  * Based on:
15765  * Ext JS Library 1.1.1
15766  * Copyright(c) 2006-2007, Ext JS, LLC.
15767  *
15768  * Originally Released Under LGPL - original licence link has changed is not relivant.
15769  *
15770  * Fork - LGPL
15771  * <script type="text/javascript">
15772  */
15773
15774 /**
15775  * @class Roo.menu.Separator
15776  * @extends Roo.menu.BaseItem
15777  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15778  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15779  * @constructor
15780  * @param {Object} config Configuration options
15781  */
15782 Roo.menu.Separator = function(config){
15783     Roo.menu.Separator.superclass.constructor.call(this, config);
15784 };
15785
15786 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15787     /**
15788      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15789      */
15790     itemCls : "x-menu-sep",
15791     /**
15792      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15793      */
15794     hideOnClick : false,
15795
15796     // private
15797     onRender : function(li){
15798         var s = document.createElement("span");
15799         s.className = this.itemCls;
15800         s.innerHTML = "&#160;";
15801         this.el = s;
15802         li.addClass("x-menu-sep-li");
15803         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15804     }
15805 });/*
15806  * Based on:
15807  * Ext JS Library 1.1.1
15808  * Copyright(c) 2006-2007, Ext JS, LLC.
15809  *
15810  * Originally Released Under LGPL - original licence link has changed is not relivant.
15811  *
15812  * Fork - LGPL
15813  * <script type="text/javascript">
15814  */
15815 /**
15816  * @class Roo.menu.Item
15817  * @extends Roo.menu.BaseItem
15818  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15819  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15820  * activation and click handling.
15821  * @constructor
15822  * Creates a new Item
15823  * @param {Object} config Configuration options
15824  */
15825 Roo.menu.Item = function(config){
15826     Roo.menu.Item.superclass.constructor.call(this, config);
15827     if(this.menu){
15828         this.menu = Roo.menu.MenuMgr.get(this.menu);
15829     }
15830 };
15831 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15832     
15833     /**
15834      * @cfg {String} text
15835      * The text to show on the menu item.
15836      */
15837     text: '',
15838      /**
15839      * @cfg {String} HTML to render in menu
15840      * The text to show on the menu item (HTML version).
15841      */
15842     html: '',
15843     /**
15844      * @cfg {String} icon
15845      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15846      */
15847     icon: undefined,
15848     /**
15849      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15850      */
15851     itemCls : "x-menu-item",
15852     /**
15853      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15854      */
15855     canActivate : true,
15856     /**
15857      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15858      */
15859     showDelay: 200,
15860     // doc'd in BaseItem
15861     hideDelay: 200,
15862
15863     // private
15864     ctype: "Roo.menu.Item",
15865     
15866     // private
15867     onRender : function(container, position){
15868         var el = document.createElement("a");
15869         el.hideFocus = true;
15870         el.unselectable = "on";
15871         el.href = this.href || "#";
15872         if(this.hrefTarget){
15873             el.target = this.hrefTarget;
15874         }
15875         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15876         
15877         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15878         
15879         el.innerHTML = String.format(
15880                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15881                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15882         this.el = el;
15883         Roo.menu.Item.superclass.onRender.call(this, container, position);
15884     },
15885
15886     /**
15887      * Sets the text to display in this menu item
15888      * @param {String} text The text to display
15889      * @param {Boolean} isHTML true to indicate text is pure html.
15890      */
15891     setText : function(text, isHTML){
15892         if (isHTML) {
15893             this.html = text;
15894         } else {
15895             this.text = text;
15896             this.html = '';
15897         }
15898         if(this.rendered){
15899             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15900      
15901             this.el.update(String.format(
15902                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15903                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15904             this.parentMenu.autoWidth();
15905         }
15906     },
15907
15908     // private
15909     handleClick : function(e){
15910         if(!this.href){ // if no link defined, stop the event automatically
15911             e.stopEvent();
15912         }
15913         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15914     },
15915
15916     // private
15917     activate : function(autoExpand){
15918         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15919             this.focus();
15920             if(autoExpand){
15921                 this.expandMenu();
15922             }
15923         }
15924         return true;
15925     },
15926
15927     // private
15928     shouldDeactivate : function(e){
15929         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15930             if(this.menu && this.menu.isVisible()){
15931                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15932             }
15933             return true;
15934         }
15935         return false;
15936     },
15937
15938     // private
15939     deactivate : function(){
15940         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15941         this.hideMenu();
15942     },
15943
15944     // private
15945     expandMenu : function(autoActivate){
15946         if(!this.disabled && this.menu){
15947             clearTimeout(this.hideTimer);
15948             delete this.hideTimer;
15949             if(!this.menu.isVisible() && !this.showTimer){
15950                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15951             }else if (this.menu.isVisible() && autoActivate){
15952                 this.menu.tryActivate(0, 1);
15953             }
15954         }
15955     },
15956
15957     // private
15958     deferExpand : function(autoActivate){
15959         delete this.showTimer;
15960         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15961         if(autoActivate){
15962             this.menu.tryActivate(0, 1);
15963         }
15964     },
15965
15966     // private
15967     hideMenu : function(){
15968         clearTimeout(this.showTimer);
15969         delete this.showTimer;
15970         if(!this.hideTimer && this.menu && this.menu.isVisible()){
15971             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15972         }
15973     },
15974
15975     // private
15976     deferHide : function(){
15977         delete this.hideTimer;
15978         this.menu.hide();
15979     }
15980 });/*
15981  * Based on:
15982  * Ext JS Library 1.1.1
15983  * Copyright(c) 2006-2007, Ext JS, LLC.
15984  *
15985  * Originally Released Under LGPL - original licence link has changed is not relivant.
15986  *
15987  * Fork - LGPL
15988  * <script type="text/javascript">
15989  */
15990  
15991 /**
15992  * @class Roo.menu.CheckItem
15993  * @extends Roo.menu.Item
15994  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
15995  * @constructor
15996  * Creates a new CheckItem
15997  * @param {Object} config Configuration options
15998  */
15999 Roo.menu.CheckItem = function(config){
16000     Roo.menu.CheckItem.superclass.constructor.call(this, config);
16001     this.addEvents({
16002         /**
16003          * @event beforecheckchange
16004          * Fires before the checked value is set, providing an opportunity to cancel if needed
16005          * @param {Roo.menu.CheckItem} this
16006          * @param {Boolean} checked The new checked value that will be set
16007          */
16008         "beforecheckchange" : true,
16009         /**
16010          * @event checkchange
16011          * Fires after the checked value has been set
16012          * @param {Roo.menu.CheckItem} this
16013          * @param {Boolean} checked The checked value that was set
16014          */
16015         "checkchange" : true
16016     });
16017     if(this.checkHandler){
16018         this.on('checkchange', this.checkHandler, this.scope);
16019     }
16020 };
16021 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
16022     /**
16023      * @cfg {String} group
16024      * All check items with the same group name will automatically be grouped into a single-select
16025      * radio button group (defaults to '')
16026      */
16027     /**
16028      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
16029      */
16030     itemCls : "x-menu-item x-menu-check-item",
16031     /**
16032      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
16033      */
16034     groupClass : "x-menu-group-item",
16035
16036     /**
16037      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
16038      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
16039      * initialized with checked = true will be rendered as checked.
16040      */
16041     checked: false,
16042
16043     // private
16044     ctype: "Roo.menu.CheckItem",
16045
16046     // private
16047     onRender : function(c){
16048         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
16049         if(this.group){
16050             this.el.addClass(this.groupClass);
16051         }
16052         Roo.menu.MenuMgr.registerCheckable(this);
16053         if(this.checked){
16054             this.checked = false;
16055             this.setChecked(true, true);
16056         }
16057     },
16058
16059     // private
16060     destroy : function(){
16061         if(this.rendered){
16062             Roo.menu.MenuMgr.unregisterCheckable(this);
16063         }
16064         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
16065     },
16066
16067     /**
16068      * Set the checked state of this item
16069      * @param {Boolean} checked The new checked value
16070      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
16071      */
16072     setChecked : function(state, suppressEvent){
16073         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
16074             if(this.container){
16075                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
16076             }
16077             this.checked = state;
16078             if(suppressEvent !== true){
16079                 this.fireEvent("checkchange", this, state);
16080             }
16081         }
16082     },
16083
16084     // private
16085     handleClick : function(e){
16086        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
16087            this.setChecked(!this.checked);
16088        }
16089        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
16090     }
16091 });/*
16092  * Based on:
16093  * Ext JS Library 1.1.1
16094  * Copyright(c) 2006-2007, Ext JS, LLC.
16095  *
16096  * Originally Released Under LGPL - original licence link has changed is not relivant.
16097  *
16098  * Fork - LGPL
16099  * <script type="text/javascript">
16100  */
16101  
16102 /**
16103  * @class Roo.menu.DateItem
16104  * @extends Roo.menu.Adapter
16105  * A menu item that wraps the {@link Roo.DatPicker} component.
16106  * @constructor
16107  * Creates a new DateItem
16108  * @param {Object} config Configuration options
16109  */
16110 Roo.menu.DateItem = function(config){
16111     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
16112     /** The Roo.DatePicker object @type Roo.DatePicker */
16113     this.picker = this.component;
16114     this.addEvents({select: true});
16115     
16116     this.picker.on("render", function(picker){
16117         picker.getEl().swallowEvent("click");
16118         picker.container.addClass("x-menu-date-item");
16119     });
16120
16121     this.picker.on("select", this.onSelect, this);
16122 };
16123
16124 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
16125     // private
16126     onSelect : function(picker, date){
16127         this.fireEvent("select", this, date, picker);
16128         Roo.menu.DateItem.superclass.handleClick.call(this);
16129     }
16130 });/*
16131  * Based on:
16132  * Ext JS Library 1.1.1
16133  * Copyright(c) 2006-2007, Ext JS, LLC.
16134  *
16135  * Originally Released Under LGPL - original licence link has changed is not relivant.
16136  *
16137  * Fork - LGPL
16138  * <script type="text/javascript">
16139  */
16140  
16141 /**
16142  * @class Roo.menu.ColorItem
16143  * @extends Roo.menu.Adapter
16144  * A menu item that wraps the {@link Roo.ColorPalette} component.
16145  * @constructor
16146  * Creates a new ColorItem
16147  * @param {Object} config Configuration options
16148  */
16149 Roo.menu.ColorItem = function(config){
16150     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
16151     /** The Roo.ColorPalette object @type Roo.ColorPalette */
16152     this.palette = this.component;
16153     this.relayEvents(this.palette, ["select"]);
16154     if(this.selectHandler){
16155         this.on('select', this.selectHandler, this.scope);
16156     }
16157 };
16158 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
16159  * Based on:
16160  * Ext JS Library 1.1.1
16161  * Copyright(c) 2006-2007, Ext JS, LLC.
16162  *
16163  * Originally Released Under LGPL - original licence link has changed is not relivant.
16164  *
16165  * Fork - LGPL
16166  * <script type="text/javascript">
16167  */
16168  
16169
16170 /**
16171  * @class Roo.menu.DateMenu
16172  * @extends Roo.menu.Menu
16173  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
16174  * @constructor
16175  * Creates a new DateMenu
16176  * @param {Object} config Configuration options
16177  */
16178 Roo.menu.DateMenu = function(config){
16179     Roo.menu.DateMenu.superclass.constructor.call(this, config);
16180     this.plain = true;
16181     var di = new Roo.menu.DateItem(config);
16182     this.add(di);
16183     /**
16184      * The {@link Roo.DatePicker} instance for this DateMenu
16185      * @type DatePicker
16186      */
16187     this.picker = di.picker;
16188     /**
16189      * @event select
16190      * @param {DatePicker} picker
16191      * @param {Date} date
16192      */
16193     this.relayEvents(di, ["select"]);
16194     this.on('beforeshow', function(){
16195         if(this.picker){
16196             this.picker.hideMonthPicker(false);
16197         }
16198     }, this);
16199 };
16200 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
16201     cls:'x-date-menu'
16202 });/*
16203  * Based on:
16204  * Ext JS Library 1.1.1
16205  * Copyright(c) 2006-2007, Ext JS, LLC.
16206  *
16207  * Originally Released Under LGPL - original licence link has changed is not relivant.
16208  *
16209  * Fork - LGPL
16210  * <script type="text/javascript">
16211  */
16212  
16213
16214 /**
16215  * @class Roo.menu.ColorMenu
16216  * @extends Roo.menu.Menu
16217  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
16218  * @constructor
16219  * Creates a new ColorMenu
16220  * @param {Object} config Configuration options
16221  */
16222 Roo.menu.ColorMenu = function(config){
16223     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
16224     this.plain = true;
16225     var ci = new Roo.menu.ColorItem(config);
16226     this.add(ci);
16227     /**
16228      * The {@link Roo.ColorPalette} instance for this ColorMenu
16229      * @type ColorPalette
16230      */
16231     this.palette = ci.palette;
16232     /**
16233      * @event select
16234      * @param {ColorPalette} palette
16235      * @param {String} color
16236      */
16237     this.relayEvents(ci, ["select"]);
16238 };
16239 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
16240  * Based on:
16241  * Ext JS Library 1.1.1
16242  * Copyright(c) 2006-2007, Ext JS, LLC.
16243  *
16244  * Originally Released Under LGPL - original licence link has changed is not relivant.
16245  *
16246  * Fork - LGPL
16247  * <script type="text/javascript">
16248  */
16249  
16250 /**
16251  * @class Roo.form.Field
16252  * @extends Roo.BoxComponent
16253  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16254  * @constructor
16255  * Creates a new Field
16256  * @param {Object} config Configuration options
16257  */
16258 Roo.form.Field = function(config){
16259     Roo.form.Field.superclass.constructor.call(this, config);
16260 };
16261
16262 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
16263     /**
16264      * @cfg {String} fieldLabel Label to use when rendering a form.
16265      */
16266        /**
16267      * @cfg {String} qtip Mouse over tip
16268      */
16269      
16270     /**
16271      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16272      */
16273     invalidClass : "x-form-invalid",
16274     /**
16275      * @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")
16276      */
16277     invalidText : "The value in this field is invalid",
16278     /**
16279      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16280      */
16281     focusClass : "x-form-focus",
16282     /**
16283      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16284       automatic validation (defaults to "keyup").
16285      */
16286     validationEvent : "keyup",
16287     /**
16288      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16289      */
16290     validateOnBlur : true,
16291     /**
16292      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16293      */
16294     validationDelay : 250,
16295     /**
16296      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16297      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16298      */
16299     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16300     /**
16301      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16302      */
16303     fieldClass : "x-form-field",
16304     /**
16305      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16306      *<pre>
16307 Value         Description
16308 -----------   ----------------------------------------------------------------------
16309 qtip          Display a quick tip when the user hovers over the field
16310 title         Display a default browser title attribute popup
16311 under         Add a block div beneath the field containing the error text
16312 side          Add an error icon to the right of the field with a popup on hover
16313 [element id]  Add the error text directly to the innerHTML of the specified element
16314 </pre>
16315      */
16316     msgTarget : 'qtip',
16317     /**
16318      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16319      */
16320     msgFx : 'normal',
16321
16322     /**
16323      * @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.
16324      */
16325     readOnly : false,
16326
16327     /**
16328      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16329      */
16330     disabled : false,
16331
16332     /**
16333      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16334      */
16335     inputType : undefined,
16336     
16337     /**
16338      * @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).
16339          */
16340         tabIndex : undefined,
16341         
16342     // private
16343     isFormField : true,
16344
16345     // private
16346     hasFocus : false,
16347     /**
16348      * @property {Roo.Element} fieldEl
16349      * Element Containing the rendered Field (with label etc.)
16350      */
16351     /**
16352      * @cfg {Mixed} value A value to initialize this field with.
16353      */
16354     value : undefined,
16355
16356     /**
16357      * @cfg {String} name The field's HTML name attribute.
16358      */
16359     /**
16360      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16361      */
16362     // private
16363     loadedValue : false,
16364      
16365      
16366         // private ??
16367         initComponent : function(){
16368         Roo.form.Field.superclass.initComponent.call(this);
16369         this.addEvents({
16370             /**
16371              * @event focus
16372              * Fires when this field receives input focus.
16373              * @param {Roo.form.Field} this
16374              */
16375             focus : true,
16376             /**
16377              * @event blur
16378              * Fires when this field loses input focus.
16379              * @param {Roo.form.Field} this
16380              */
16381             blur : true,
16382             /**
16383              * @event specialkey
16384              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16385              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16386              * @param {Roo.form.Field} this
16387              * @param {Roo.EventObject} e The event object
16388              */
16389             specialkey : true,
16390             /**
16391              * @event change
16392              * Fires just before the field blurs if the field value has changed.
16393              * @param {Roo.form.Field} this
16394              * @param {Mixed} newValue The new value
16395              * @param {Mixed} oldValue The original value
16396              */
16397             change : true,
16398             /**
16399              * @event invalid
16400              * Fires after the field has been marked as invalid.
16401              * @param {Roo.form.Field} this
16402              * @param {String} msg The validation message
16403              */
16404             invalid : true,
16405             /**
16406              * @event valid
16407              * Fires after the field has been validated with no errors.
16408              * @param {Roo.form.Field} this
16409              */
16410             valid : true,
16411              /**
16412              * @event keyup
16413              * Fires after the key up
16414              * @param {Roo.form.Field} this
16415              * @param {Roo.EventObject}  e The event Object
16416              */
16417             keyup : true
16418         });
16419     },
16420
16421     /**
16422      * Returns the name attribute of the field if available
16423      * @return {String} name The field name
16424      */
16425     getName: function(){
16426          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16427     },
16428
16429     // private
16430     onRender : function(ct, position){
16431         Roo.form.Field.superclass.onRender.call(this, ct, position);
16432         if(!this.el){
16433             var cfg = this.getAutoCreate();
16434             if(!cfg.name){
16435                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16436             }
16437             if (!cfg.name.length) {
16438                 delete cfg.name;
16439             }
16440             if(this.inputType){
16441                 cfg.type = this.inputType;
16442             }
16443             this.el = ct.createChild(cfg, position);
16444         }
16445         var type = this.el.dom.type;
16446         if(type){
16447             if(type == 'password'){
16448                 type = 'text';
16449             }
16450             this.el.addClass('x-form-'+type);
16451         }
16452         if(this.readOnly){
16453             this.el.dom.readOnly = true;
16454         }
16455         if(this.tabIndex !== undefined){
16456             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16457         }
16458
16459         this.el.addClass([this.fieldClass, this.cls]);
16460         this.initValue();
16461     },
16462
16463     /**
16464      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16465      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16466      * @return {Roo.form.Field} this
16467      */
16468     applyTo : function(target){
16469         this.allowDomMove = false;
16470         this.el = Roo.get(target);
16471         this.render(this.el.dom.parentNode);
16472         return this;
16473     },
16474
16475     // private
16476     initValue : function(){
16477         if(this.value !== undefined){
16478             this.setValue(this.value);
16479         }else if(this.el.dom.value.length > 0){
16480             this.setValue(this.el.dom.value);
16481         }
16482     },
16483
16484     /**
16485      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16486      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16487      */
16488     isDirty : function() {
16489         if(this.disabled) {
16490             return false;
16491         }
16492         return String(this.getValue()) !== String(this.originalValue);
16493     },
16494
16495     /**
16496      * stores the current value in loadedValue
16497      */
16498     resetHasChanged : function()
16499     {
16500         this.loadedValue = String(this.getValue());
16501     },
16502     /**
16503      * checks the current value against the 'loaded' value.
16504      * Note - will return false if 'resetHasChanged' has not been called first.
16505      */
16506     hasChanged : function()
16507     {
16508         if(this.disabled || this.readOnly) {
16509             return false;
16510         }
16511         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16512     },
16513     
16514     
16515     
16516     // private
16517     afterRender : function(){
16518         Roo.form.Field.superclass.afterRender.call(this);
16519         this.initEvents();
16520     },
16521
16522     // private
16523     fireKey : function(e){
16524         //Roo.log('field ' + e.getKey());
16525         if(e.isNavKeyPress()){
16526             this.fireEvent("specialkey", this, e);
16527         }
16528     },
16529
16530     /**
16531      * Resets the current field value to the originally loaded value and clears any validation messages
16532      */
16533     reset : function(){
16534         this.setValue(this.resetValue);
16535         this.originalValue = this.getValue();
16536         this.clearInvalid();
16537     },
16538
16539     // private
16540     initEvents : function(){
16541         // safari killled keypress - so keydown is now used..
16542         this.el.on("keydown" , this.fireKey,  this);
16543         this.el.on("focus", this.onFocus,  this);
16544         this.el.on("blur", this.onBlur,  this);
16545         this.el.relayEvent('keyup', this);
16546
16547         // reference to original value for reset
16548         this.originalValue = this.getValue();
16549         this.resetValue =  this.getValue();
16550     },
16551
16552     // private
16553     onFocus : function(){
16554         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16555             this.el.addClass(this.focusClass);
16556         }
16557         if(!this.hasFocus){
16558             this.hasFocus = true;
16559             this.startValue = this.getValue();
16560             this.fireEvent("focus", this);
16561         }
16562     },
16563
16564     beforeBlur : Roo.emptyFn,
16565
16566     // private
16567     onBlur : function(){
16568         this.beforeBlur();
16569         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16570             this.el.removeClass(this.focusClass);
16571         }
16572         this.hasFocus = false;
16573         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16574             this.validate();
16575         }
16576         var v = this.getValue();
16577         if(String(v) !== String(this.startValue)){
16578             this.fireEvent('change', this, v, this.startValue);
16579         }
16580         this.fireEvent("blur", this);
16581     },
16582
16583     /**
16584      * Returns whether or not the field value is currently valid
16585      * @param {Boolean} preventMark True to disable marking the field invalid
16586      * @return {Boolean} True if the value is valid, else false
16587      */
16588     isValid : function(preventMark){
16589         if(this.disabled){
16590             return true;
16591         }
16592         var restore = this.preventMark;
16593         this.preventMark = preventMark === true;
16594         var v = this.validateValue(this.processValue(this.getRawValue()));
16595         this.preventMark = restore;
16596         return v;
16597     },
16598
16599     /**
16600      * Validates the field value
16601      * @return {Boolean} True if the value is valid, else false
16602      */
16603     validate : function(){
16604         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16605             this.clearInvalid();
16606             return true;
16607         }
16608         return false;
16609     },
16610
16611     processValue : function(value){
16612         return value;
16613     },
16614
16615     // private
16616     // Subclasses should provide the validation implementation by overriding this
16617     validateValue : function(value){
16618         return true;
16619     },
16620
16621     /**
16622      * Mark this field as invalid
16623      * @param {String} msg The validation message
16624      */
16625     markInvalid : function(msg){
16626         if(!this.rendered || this.preventMark){ // not rendered
16627             return;
16628         }
16629         
16630         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16631         
16632         obj.el.addClass(this.invalidClass);
16633         msg = msg || this.invalidText;
16634         switch(this.msgTarget){
16635             case 'qtip':
16636                 obj.el.dom.qtip = msg;
16637                 obj.el.dom.qclass = 'x-form-invalid-tip';
16638                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16639                     Roo.QuickTips.enable();
16640                 }
16641                 break;
16642             case 'title':
16643                 this.el.dom.title = msg;
16644                 break;
16645             case 'under':
16646                 if(!this.errorEl){
16647                     var elp = this.el.findParent('.x-form-element', 5, true);
16648                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16649                     this.errorEl.setWidth(elp.getWidth(true)-20);
16650                 }
16651                 this.errorEl.update(msg);
16652                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16653                 break;
16654             case 'side':
16655                 if(!this.errorIcon){
16656                     var elp = this.el.findParent('.x-form-element', 5, true);
16657                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16658                 }
16659                 this.alignErrorIcon();
16660                 this.errorIcon.dom.qtip = msg;
16661                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16662                 this.errorIcon.show();
16663                 this.on('resize', this.alignErrorIcon, this);
16664                 break;
16665             default:
16666                 var t = Roo.getDom(this.msgTarget);
16667                 t.innerHTML = msg;
16668                 t.style.display = this.msgDisplay;
16669                 break;
16670         }
16671         this.fireEvent('invalid', this, msg);
16672     },
16673
16674     // private
16675     alignErrorIcon : function(){
16676         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16677     },
16678
16679     /**
16680      * Clear any invalid styles/messages for this field
16681      */
16682     clearInvalid : function(){
16683         if(!this.rendered || this.preventMark){ // not rendered
16684             return;
16685         }
16686         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16687         
16688         obj.el.removeClass(this.invalidClass);
16689         switch(this.msgTarget){
16690             case 'qtip':
16691                 obj.el.dom.qtip = '';
16692                 break;
16693             case 'title':
16694                 this.el.dom.title = '';
16695                 break;
16696             case 'under':
16697                 if(this.errorEl){
16698                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16699                 }
16700                 break;
16701             case 'side':
16702                 if(this.errorIcon){
16703                     this.errorIcon.dom.qtip = '';
16704                     this.errorIcon.hide();
16705                     this.un('resize', this.alignErrorIcon, this);
16706                 }
16707                 break;
16708             default:
16709                 var t = Roo.getDom(this.msgTarget);
16710                 t.innerHTML = '';
16711                 t.style.display = 'none';
16712                 break;
16713         }
16714         this.fireEvent('valid', this);
16715     },
16716
16717     /**
16718      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16719      * @return {Mixed} value The field value
16720      */
16721     getRawValue : function(){
16722         var v = this.el.getValue();
16723         
16724         return v;
16725     },
16726
16727     /**
16728      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16729      * @return {Mixed} value The field value
16730      */
16731     getValue : function(){
16732         var v = this.el.getValue();
16733          
16734         return v;
16735     },
16736
16737     /**
16738      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16739      * @param {Mixed} value The value to set
16740      */
16741     setRawValue : function(v){
16742         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16743     },
16744
16745     /**
16746      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16747      * @param {Mixed} value The value to set
16748      */
16749     setValue : function(v){
16750         this.value = v;
16751         if(this.rendered){
16752             this.el.dom.value = (v === null || v === undefined ? '' : v);
16753              this.validate();
16754         }
16755     },
16756
16757     adjustSize : function(w, h){
16758         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16759         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16760         return s;
16761     },
16762
16763     adjustWidth : function(tag, w){
16764         tag = tag.toLowerCase();
16765         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16766             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16767                 if(tag == 'input'){
16768                     return w + 2;
16769                 }
16770                 if(tag == 'textarea'){
16771                     return w-2;
16772                 }
16773             }else if(Roo.isOpera){
16774                 if(tag == 'input'){
16775                     return w + 2;
16776                 }
16777                 if(tag == 'textarea'){
16778                     return w-2;
16779                 }
16780             }
16781         }
16782         return w;
16783     }
16784 });
16785
16786
16787 // anything other than normal should be considered experimental
16788 Roo.form.Field.msgFx = {
16789     normal : {
16790         show: function(msgEl, f){
16791             msgEl.setDisplayed('block');
16792         },
16793
16794         hide : function(msgEl, f){
16795             msgEl.setDisplayed(false).update('');
16796         }
16797     },
16798
16799     slide : {
16800         show: function(msgEl, f){
16801             msgEl.slideIn('t', {stopFx:true});
16802         },
16803
16804         hide : function(msgEl, f){
16805             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16806         }
16807     },
16808
16809     slideRight : {
16810         show: function(msgEl, f){
16811             msgEl.fixDisplay();
16812             msgEl.alignTo(f.el, 'tl-tr');
16813             msgEl.slideIn('l', {stopFx:true});
16814         },
16815
16816         hide : function(msgEl, f){
16817             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16818         }
16819     }
16820 };/*
16821  * Based on:
16822  * Ext JS Library 1.1.1
16823  * Copyright(c) 2006-2007, Ext JS, LLC.
16824  *
16825  * Originally Released Under LGPL - original licence link has changed is not relivant.
16826  *
16827  * Fork - LGPL
16828  * <script type="text/javascript">
16829  */
16830  
16831
16832 /**
16833  * @class Roo.form.TextField
16834  * @extends Roo.form.Field
16835  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16836  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16837  * @constructor
16838  * Creates a new TextField
16839  * @param {Object} config Configuration options
16840  */
16841 Roo.form.TextField = function(config){
16842     Roo.form.TextField.superclass.constructor.call(this, config);
16843     this.addEvents({
16844         /**
16845          * @event autosize
16846          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16847          * according to the default logic, but this event provides a hook for the developer to apply additional
16848          * logic at runtime to resize the field if needed.
16849              * @param {Roo.form.Field} this This text field
16850              * @param {Number} width The new field width
16851              */
16852         autosize : true
16853     });
16854 };
16855
16856 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16857     /**
16858      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16859      */
16860     grow : false,
16861     /**
16862      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16863      */
16864     growMin : 30,
16865     /**
16866      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16867      */
16868     growMax : 800,
16869     /**
16870      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16871      */
16872     vtype : null,
16873     /**
16874      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16875      */
16876     maskRe : null,
16877     /**
16878      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16879      */
16880     disableKeyFilter : false,
16881     /**
16882      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16883      */
16884     allowBlank : true,
16885     /**
16886      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16887      */
16888     minLength : 0,
16889     /**
16890      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16891      */
16892     maxLength : Number.MAX_VALUE,
16893     /**
16894      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16895      */
16896     minLengthText : "The minimum length for this field is {0}",
16897     /**
16898      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16899      */
16900     maxLengthText : "The maximum length for this field is {0}",
16901     /**
16902      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16903      */
16904     selectOnFocus : false,
16905     /**
16906      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16907      */
16908     blankText : "This field is required",
16909     /**
16910      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16911      * If available, this function will be called only after the basic validators all return true, and will be passed the
16912      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16913      */
16914     validator : null,
16915     /**
16916      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16917      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16918      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
16919      */
16920     regex : null,
16921     /**
16922      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16923      */
16924     regexText : "",
16925     /**
16926      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16927      */
16928     emptyText : null,
16929    
16930
16931     // private
16932     initEvents : function()
16933     {
16934         if (this.emptyText) {
16935             this.el.attr('placeholder', this.emptyText);
16936         }
16937         
16938         Roo.form.TextField.superclass.initEvents.call(this);
16939         if(this.validationEvent == 'keyup'){
16940             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16941             this.el.on('keyup', this.filterValidation, this);
16942         }
16943         else if(this.validationEvent !== false){
16944             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16945         }
16946         
16947         if(this.selectOnFocus){
16948             this.on("focus", this.preFocus, this);
16949             
16950         }
16951         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16952             this.el.on("keypress", this.filterKeys, this);
16953         }
16954         if(this.grow){
16955             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
16956             this.el.on("click", this.autoSize,  this);
16957         }
16958         if(this.el.is('input[type=password]') && Roo.isSafari){
16959             this.el.on('keydown', this.SafariOnKeyDown, this);
16960         }
16961     },
16962
16963     processValue : function(value){
16964         if(this.stripCharsRe){
16965             var newValue = value.replace(this.stripCharsRe, '');
16966             if(newValue !== value){
16967                 this.setRawValue(newValue);
16968                 return newValue;
16969             }
16970         }
16971         return value;
16972     },
16973
16974     filterValidation : function(e){
16975         if(!e.isNavKeyPress()){
16976             this.validationTask.delay(this.validationDelay);
16977         }
16978     },
16979
16980     // private
16981     onKeyUp : function(e){
16982         if(!e.isNavKeyPress()){
16983             this.autoSize();
16984         }
16985     },
16986
16987     /**
16988      * Resets the current field value to the originally-loaded value and clears any validation messages.
16989      *  
16990      */
16991     reset : function(){
16992         Roo.form.TextField.superclass.reset.call(this);
16993        
16994     },
16995
16996     
16997     // private
16998     preFocus : function(){
16999         
17000         if(this.selectOnFocus){
17001             this.el.dom.select();
17002         }
17003     },
17004
17005     
17006     // private
17007     filterKeys : function(e){
17008         var k = e.getKey();
17009         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
17010             return;
17011         }
17012         var c = e.getCharCode(), cc = String.fromCharCode(c);
17013         if(Roo.isIE && (e.isSpecialKey() || !cc)){
17014             return;
17015         }
17016         if(!this.maskRe.test(cc)){
17017             e.stopEvent();
17018         }
17019     },
17020
17021     setValue : function(v){
17022         
17023         Roo.form.TextField.superclass.setValue.apply(this, arguments);
17024         
17025         this.autoSize();
17026     },
17027
17028     /**
17029      * Validates a value according to the field's validation rules and marks the field as invalid
17030      * if the validation fails
17031      * @param {Mixed} value The value to validate
17032      * @return {Boolean} True if the value is valid, else false
17033      */
17034     validateValue : function(value){
17035         if(value.length < 1)  { // if it's blank
17036              if(this.allowBlank){
17037                 this.clearInvalid();
17038                 return true;
17039              }else{
17040                 this.markInvalid(this.blankText);
17041                 return false;
17042              }
17043         }
17044         if(value.length < this.minLength){
17045             this.markInvalid(String.format(this.minLengthText, this.minLength));
17046             return false;
17047         }
17048         if(value.length > this.maxLength){
17049             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
17050             return false;
17051         }
17052         if(this.vtype){
17053             var vt = Roo.form.VTypes;
17054             if(!vt[this.vtype](value, this)){
17055                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
17056                 return false;
17057             }
17058         }
17059         if(typeof this.validator == "function"){
17060             var msg = this.validator(value);
17061             if(msg !== true){
17062                 this.markInvalid(msg);
17063                 return false;
17064             }
17065         }
17066         if(this.regex && !this.regex.test(value)){
17067             this.markInvalid(this.regexText);
17068             return false;
17069         }
17070         return true;
17071     },
17072
17073     /**
17074      * Selects text in this field
17075      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
17076      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
17077      */
17078     selectText : function(start, end){
17079         var v = this.getRawValue();
17080         if(v.length > 0){
17081             start = start === undefined ? 0 : start;
17082             end = end === undefined ? v.length : end;
17083             var d = this.el.dom;
17084             if(d.setSelectionRange){
17085                 d.setSelectionRange(start, end);
17086             }else if(d.createTextRange){
17087                 var range = d.createTextRange();
17088                 range.moveStart("character", start);
17089                 range.moveEnd("character", v.length-end);
17090                 range.select();
17091             }
17092         }
17093     },
17094
17095     /**
17096      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
17097      * This only takes effect if grow = true, and fires the autosize event.
17098      */
17099     autoSize : function(){
17100         if(!this.grow || !this.rendered){
17101             return;
17102         }
17103         if(!this.metrics){
17104             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
17105         }
17106         var el = this.el;
17107         var v = el.dom.value;
17108         var d = document.createElement('div');
17109         d.appendChild(document.createTextNode(v));
17110         v = d.innerHTML;
17111         d = null;
17112         v += "&#160;";
17113         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
17114         this.el.setWidth(w);
17115         this.fireEvent("autosize", this, w);
17116     },
17117     
17118     // private
17119     SafariOnKeyDown : function(event)
17120     {
17121         // this is a workaround for a password hang bug on chrome/ webkit.
17122         
17123         var isSelectAll = false;
17124         
17125         if(this.el.dom.selectionEnd > 0){
17126             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
17127         }
17128         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
17129             event.preventDefault();
17130             this.setValue('');
17131             return;
17132         }
17133         
17134         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
17135             
17136             event.preventDefault();
17137             // this is very hacky as keydown always get's upper case.
17138             
17139             var cc = String.fromCharCode(event.getCharCode());
17140             
17141             
17142             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
17143             
17144         }
17145         
17146         
17147     }
17148 });/*
17149  * Based on:
17150  * Ext JS Library 1.1.1
17151  * Copyright(c) 2006-2007, Ext JS, LLC.
17152  *
17153  * Originally Released Under LGPL - original licence link has changed is not relivant.
17154  *
17155  * Fork - LGPL
17156  * <script type="text/javascript">
17157  */
17158  
17159 /**
17160  * @class Roo.form.Hidden
17161  * @extends Roo.form.TextField
17162  * Simple Hidden element used on forms 
17163  * 
17164  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
17165  * 
17166  * @constructor
17167  * Creates a new Hidden form element.
17168  * @param {Object} config Configuration options
17169  */
17170
17171
17172
17173 // easy hidden field...
17174 Roo.form.Hidden = function(config){
17175     Roo.form.Hidden.superclass.constructor.call(this, config);
17176 };
17177   
17178 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
17179     fieldLabel:      '',
17180     inputType:      'hidden',
17181     width:          50,
17182     allowBlank:     true,
17183     labelSeparator: '',
17184     hidden:         true,
17185     itemCls :       'x-form-item-display-none'
17186
17187
17188 });
17189
17190
17191 /*
17192  * Based on:
17193  * Ext JS Library 1.1.1
17194  * Copyright(c) 2006-2007, Ext JS, LLC.
17195  *
17196  * Originally Released Under LGPL - original licence link has changed is not relivant.
17197  *
17198  * Fork - LGPL
17199  * <script type="text/javascript">
17200  */
17201  
17202 /**
17203  * @class Roo.form.TriggerField
17204  * @extends Roo.form.TextField
17205  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
17206  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
17207  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
17208  * for which you can provide a custom implementation.  For example:
17209  * <pre><code>
17210 var trigger = new Roo.form.TriggerField();
17211 trigger.onTriggerClick = myTriggerFn;
17212 trigger.applyTo('my-field');
17213 </code></pre>
17214  *
17215  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
17216  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
17217  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
17218  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
17219  * @constructor
17220  * Create a new TriggerField.
17221  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
17222  * to the base TextField)
17223  */
17224 Roo.form.TriggerField = function(config){
17225     this.mimicing = false;
17226     Roo.form.TriggerField.superclass.constructor.call(this, config);
17227 };
17228
17229 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
17230     /**
17231      * @cfg {String} triggerClass A CSS class to apply to the trigger
17232      */
17233     /**
17234      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17235      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
17236      */
17237     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
17238     /**
17239      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
17240      */
17241     hideTrigger:false,
17242
17243     /** @cfg {Boolean} grow @hide */
17244     /** @cfg {Number} growMin @hide */
17245     /** @cfg {Number} growMax @hide */
17246
17247     /**
17248      * @hide 
17249      * @method
17250      */
17251     autoSize: Roo.emptyFn,
17252     // private
17253     monitorTab : true,
17254     // private
17255     deferHeight : true,
17256
17257     
17258     actionMode : 'wrap',
17259     // private
17260     onResize : function(w, h){
17261         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17262         if(typeof w == 'number'){
17263             var x = w - this.trigger.getWidth();
17264             this.el.setWidth(this.adjustWidth('input', x));
17265             this.trigger.setStyle('left', x+'px');
17266         }
17267     },
17268
17269     // private
17270     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17271
17272     // private
17273     getResizeEl : function(){
17274         return this.wrap;
17275     },
17276
17277     // private
17278     getPositionEl : function(){
17279         return this.wrap;
17280     },
17281
17282     // private
17283     alignErrorIcon : function(){
17284         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17285     },
17286
17287     // private
17288     onRender : function(ct, position){
17289         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17290         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17291         this.trigger = this.wrap.createChild(this.triggerConfig ||
17292                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17293         if(this.hideTrigger){
17294             this.trigger.setDisplayed(false);
17295         }
17296         this.initTrigger();
17297         if(!this.width){
17298             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17299         }
17300     },
17301
17302     // private
17303     initTrigger : function(){
17304         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17305         this.trigger.addClassOnOver('x-form-trigger-over');
17306         this.trigger.addClassOnClick('x-form-trigger-click');
17307     },
17308
17309     // private
17310     onDestroy : function(){
17311         if(this.trigger){
17312             this.trigger.removeAllListeners();
17313             this.trigger.remove();
17314         }
17315         if(this.wrap){
17316             this.wrap.remove();
17317         }
17318         Roo.form.TriggerField.superclass.onDestroy.call(this);
17319     },
17320
17321     // private
17322     onFocus : function(){
17323         Roo.form.TriggerField.superclass.onFocus.call(this);
17324         if(!this.mimicing){
17325             this.wrap.addClass('x-trigger-wrap-focus');
17326             this.mimicing = true;
17327             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17328             if(this.monitorTab){
17329                 this.el.on("keydown", this.checkTab, this);
17330             }
17331         }
17332     },
17333
17334     // private
17335     checkTab : function(e){
17336         if(e.getKey() == e.TAB){
17337             this.triggerBlur();
17338         }
17339     },
17340
17341     // private
17342     onBlur : function(){
17343         // do nothing
17344     },
17345
17346     // private
17347     mimicBlur : function(e, t){
17348         if(!this.wrap.contains(t) && this.validateBlur()){
17349             this.triggerBlur();
17350         }
17351     },
17352
17353     // private
17354     triggerBlur : function(){
17355         this.mimicing = false;
17356         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17357         if(this.monitorTab){
17358             this.el.un("keydown", this.checkTab, this);
17359         }
17360         this.wrap.removeClass('x-trigger-wrap-focus');
17361         Roo.form.TriggerField.superclass.onBlur.call(this);
17362     },
17363
17364     // private
17365     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17366     validateBlur : function(e, t){
17367         return true;
17368     },
17369
17370     // private
17371     onDisable : function(){
17372         Roo.form.TriggerField.superclass.onDisable.call(this);
17373         if(this.wrap){
17374             this.wrap.addClass('x-item-disabled');
17375         }
17376     },
17377
17378     // private
17379     onEnable : function(){
17380         Roo.form.TriggerField.superclass.onEnable.call(this);
17381         if(this.wrap){
17382             this.wrap.removeClass('x-item-disabled');
17383         }
17384     },
17385
17386     // private
17387     onShow : function(){
17388         var ae = this.getActionEl();
17389         
17390         if(ae){
17391             ae.dom.style.display = '';
17392             ae.dom.style.visibility = 'visible';
17393         }
17394     },
17395
17396     // private
17397     
17398     onHide : function(){
17399         var ae = this.getActionEl();
17400         ae.dom.style.display = 'none';
17401     },
17402
17403     /**
17404      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17405      * by an implementing function.
17406      * @method
17407      * @param {EventObject} e
17408      */
17409     onTriggerClick : Roo.emptyFn
17410 });
17411
17412 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17413 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17414 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17415 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17416     initComponent : function(){
17417         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17418
17419         this.triggerConfig = {
17420             tag:'span', cls:'x-form-twin-triggers', cn:[
17421             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17422             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17423         ]};
17424     },
17425
17426     getTrigger : function(index){
17427         return this.triggers[index];
17428     },
17429
17430     initTrigger : function(){
17431         var ts = this.trigger.select('.x-form-trigger', true);
17432         this.wrap.setStyle('overflow', 'hidden');
17433         var triggerField = this;
17434         ts.each(function(t, all, index){
17435             t.hide = function(){
17436                 var w = triggerField.wrap.getWidth();
17437                 this.dom.style.display = 'none';
17438                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17439             };
17440             t.show = function(){
17441                 var w = triggerField.wrap.getWidth();
17442                 this.dom.style.display = '';
17443                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17444             };
17445             var triggerIndex = 'Trigger'+(index+1);
17446
17447             if(this['hide'+triggerIndex]){
17448                 t.dom.style.display = 'none';
17449             }
17450             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17451             t.addClassOnOver('x-form-trigger-over');
17452             t.addClassOnClick('x-form-trigger-click');
17453         }, this);
17454         this.triggers = ts.elements;
17455     },
17456
17457     onTrigger1Click : Roo.emptyFn,
17458     onTrigger2Click : Roo.emptyFn
17459 });/*
17460  * Based on:
17461  * Ext JS Library 1.1.1
17462  * Copyright(c) 2006-2007, Ext JS, LLC.
17463  *
17464  * Originally Released Under LGPL - original licence link has changed is not relivant.
17465  *
17466  * Fork - LGPL
17467  * <script type="text/javascript">
17468  */
17469  
17470 /**
17471  * @class Roo.form.TextArea
17472  * @extends Roo.form.TextField
17473  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17474  * support for auto-sizing.
17475  * @constructor
17476  * Creates a new TextArea
17477  * @param {Object} config Configuration options
17478  */
17479 Roo.form.TextArea = function(config){
17480     Roo.form.TextArea.superclass.constructor.call(this, config);
17481     // these are provided exchanges for backwards compat
17482     // minHeight/maxHeight were replaced by growMin/growMax to be
17483     // compatible with TextField growing config values
17484     if(this.minHeight !== undefined){
17485         this.growMin = this.minHeight;
17486     }
17487     if(this.maxHeight !== undefined){
17488         this.growMax = this.maxHeight;
17489     }
17490 };
17491
17492 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17493     /**
17494      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17495      */
17496     growMin : 60,
17497     /**
17498      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17499      */
17500     growMax: 1000,
17501     /**
17502      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17503      * in the field (equivalent to setting overflow: hidden, defaults to false)
17504      */
17505     preventScrollbars: false,
17506     /**
17507      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17508      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17509      */
17510
17511     // private
17512     onRender : function(ct, position){
17513         if(!this.el){
17514             this.defaultAutoCreate = {
17515                 tag: "textarea",
17516                 style:"width:300px;height:60px;",
17517                 autocomplete: "new-password"
17518             };
17519         }
17520         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17521         if(this.grow){
17522             this.textSizeEl = Roo.DomHelper.append(document.body, {
17523                 tag: "pre", cls: "x-form-grow-sizer"
17524             });
17525             if(this.preventScrollbars){
17526                 this.el.setStyle("overflow", "hidden");
17527             }
17528             this.el.setHeight(this.growMin);
17529         }
17530     },
17531
17532     onDestroy : function(){
17533         if(this.textSizeEl){
17534             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17535         }
17536         Roo.form.TextArea.superclass.onDestroy.call(this);
17537     },
17538
17539     // private
17540     onKeyUp : function(e){
17541         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17542             this.autoSize();
17543         }
17544     },
17545
17546     /**
17547      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17548      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17549      */
17550     autoSize : function(){
17551         if(!this.grow || !this.textSizeEl){
17552             return;
17553         }
17554         var el = this.el;
17555         var v = el.dom.value;
17556         var ts = this.textSizeEl;
17557
17558         ts.innerHTML = '';
17559         ts.appendChild(document.createTextNode(v));
17560         v = ts.innerHTML;
17561
17562         Roo.fly(ts).setWidth(this.el.getWidth());
17563         if(v.length < 1){
17564             v = "&#160;&#160;";
17565         }else{
17566             if(Roo.isIE){
17567                 v = v.replace(/\n/g, '<p>&#160;</p>');
17568             }
17569             v += "&#160;\n&#160;";
17570         }
17571         ts.innerHTML = v;
17572         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17573         if(h != this.lastHeight){
17574             this.lastHeight = h;
17575             this.el.setHeight(h);
17576             this.fireEvent("autosize", this, h);
17577         }
17578     }
17579 });/*
17580  * Based on:
17581  * Ext JS Library 1.1.1
17582  * Copyright(c) 2006-2007, Ext JS, LLC.
17583  *
17584  * Originally Released Under LGPL - original licence link has changed is not relivant.
17585  *
17586  * Fork - LGPL
17587  * <script type="text/javascript">
17588  */
17589  
17590
17591 /**
17592  * @class Roo.form.NumberField
17593  * @extends Roo.form.TextField
17594  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17595  * @constructor
17596  * Creates a new NumberField
17597  * @param {Object} config Configuration options
17598  */
17599 Roo.form.NumberField = function(config){
17600     Roo.form.NumberField.superclass.constructor.call(this, config);
17601 };
17602
17603 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17604     /**
17605      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17606      */
17607     fieldClass: "x-form-field x-form-num-field",
17608     /**
17609      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17610      */
17611     allowDecimals : true,
17612     /**
17613      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17614      */
17615     decimalSeparator : ".",
17616     /**
17617      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17618      */
17619     decimalPrecision : 2,
17620     /**
17621      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17622      */
17623     allowNegative : true,
17624     /**
17625      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17626      */
17627     minValue : Number.NEGATIVE_INFINITY,
17628     /**
17629      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17630      */
17631     maxValue : Number.MAX_VALUE,
17632     /**
17633      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17634      */
17635     minText : "The minimum value for this field is {0}",
17636     /**
17637      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17638      */
17639     maxText : "The maximum value for this field is {0}",
17640     /**
17641      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17642      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17643      */
17644     nanText : "{0} is not a valid number",
17645
17646     // private
17647     initEvents : function(){
17648         Roo.form.NumberField.superclass.initEvents.call(this);
17649         var allowed = "0123456789";
17650         if(this.allowDecimals){
17651             allowed += this.decimalSeparator;
17652         }
17653         if(this.allowNegative){
17654             allowed += "-";
17655         }
17656         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17657         var keyPress = function(e){
17658             var k = e.getKey();
17659             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17660                 return;
17661             }
17662             var c = e.getCharCode();
17663             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17664                 e.stopEvent();
17665             }
17666         };
17667         this.el.on("keypress", keyPress, this);
17668     },
17669
17670     // private
17671     validateValue : function(value){
17672         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17673             return false;
17674         }
17675         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17676              return true;
17677         }
17678         var num = this.parseValue(value);
17679         if(isNaN(num)){
17680             this.markInvalid(String.format(this.nanText, value));
17681             return false;
17682         }
17683         if(num < this.minValue){
17684             this.markInvalid(String.format(this.minText, this.minValue));
17685             return false;
17686         }
17687         if(num > this.maxValue){
17688             this.markInvalid(String.format(this.maxText, this.maxValue));
17689             return false;
17690         }
17691         return true;
17692     },
17693
17694     getValue : function(){
17695         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17696     },
17697
17698     // private
17699     parseValue : function(value){
17700         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17701         return isNaN(value) ? '' : value;
17702     },
17703
17704     // private
17705     fixPrecision : function(value){
17706         var nan = isNaN(value);
17707         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17708             return nan ? '' : value;
17709         }
17710         return parseFloat(value).toFixed(this.decimalPrecision);
17711     },
17712
17713     setValue : function(v){
17714         v = this.fixPrecision(v);
17715         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17716     },
17717
17718     // private
17719     decimalPrecisionFcn : function(v){
17720         return Math.floor(v);
17721     },
17722
17723     beforeBlur : function(){
17724         var v = this.parseValue(this.getRawValue());
17725         if(v){
17726             this.setValue(v);
17727         }
17728     }
17729 });/*
17730  * Based on:
17731  * Ext JS Library 1.1.1
17732  * Copyright(c) 2006-2007, Ext JS, LLC.
17733  *
17734  * Originally Released Under LGPL - original licence link has changed is not relivant.
17735  *
17736  * Fork - LGPL
17737  * <script type="text/javascript">
17738  */
17739  
17740 /**
17741  * @class Roo.form.DateField
17742  * @extends Roo.form.TriggerField
17743  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17744 * @constructor
17745 * Create a new DateField
17746 * @param {Object} config
17747  */
17748 Roo.form.DateField = function(config){
17749     Roo.form.DateField.superclass.constructor.call(this, config);
17750     
17751       this.addEvents({
17752          
17753         /**
17754          * @event select
17755          * Fires when a date is selected
17756              * @param {Roo.form.DateField} combo This combo box
17757              * @param {Date} date The date selected
17758              */
17759         'select' : true
17760          
17761     });
17762     
17763     
17764     if(typeof this.minValue == "string") {
17765         this.minValue = this.parseDate(this.minValue);
17766     }
17767     if(typeof this.maxValue == "string") {
17768         this.maxValue = this.parseDate(this.maxValue);
17769     }
17770     this.ddMatch = null;
17771     if(this.disabledDates){
17772         var dd = this.disabledDates;
17773         var re = "(?:";
17774         for(var i = 0; i < dd.length; i++){
17775             re += dd[i];
17776             if(i != dd.length-1) {
17777                 re += "|";
17778             }
17779         }
17780         this.ddMatch = new RegExp(re + ")");
17781     }
17782 };
17783
17784 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17785     /**
17786      * @cfg {String} format
17787      * The default date format string which can be overriden for localization support.  The format must be
17788      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17789      */
17790     format : "m/d/y",
17791     /**
17792      * @cfg {String} altFormats
17793      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17794      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17795      */
17796     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17797     /**
17798      * @cfg {Array} disabledDays
17799      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17800      */
17801     disabledDays : null,
17802     /**
17803      * @cfg {String} disabledDaysText
17804      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17805      */
17806     disabledDaysText : "Disabled",
17807     /**
17808      * @cfg {Array} disabledDates
17809      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17810      * expression so they are very powerful. Some examples:
17811      * <ul>
17812      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17813      * <li>["03/08", "09/16"] would disable those days for every year</li>
17814      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17815      * <li>["03/../2006"] would disable every day in March 2006</li>
17816      * <li>["^03"] would disable every day in every March</li>
17817      * </ul>
17818      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17819      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17820      */
17821     disabledDates : null,
17822     /**
17823      * @cfg {String} disabledDatesText
17824      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17825      */
17826     disabledDatesText : "Disabled",
17827     /**
17828      * @cfg {Date/String} minValue
17829      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17830      * valid format (defaults to null).
17831      */
17832     minValue : null,
17833     /**
17834      * @cfg {Date/String} maxValue
17835      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17836      * valid format (defaults to null).
17837      */
17838     maxValue : null,
17839     /**
17840      * @cfg {String} minText
17841      * The error text to display when the date in the cell is before minValue (defaults to
17842      * 'The date in this field must be after {minValue}').
17843      */
17844     minText : "The date in this field must be equal to or after {0}",
17845     /**
17846      * @cfg {String} maxText
17847      * The error text to display when the date in the cell is after maxValue (defaults to
17848      * 'The date in this field must be before {maxValue}').
17849      */
17850     maxText : "The date in this field must be equal to or before {0}",
17851     /**
17852      * @cfg {String} invalidText
17853      * The error text to display when the date in the field is invalid (defaults to
17854      * '{value} is not a valid date - it must be in the format {format}').
17855      */
17856     invalidText : "{0} is not a valid date - it must be in the format {1}",
17857     /**
17858      * @cfg {String} triggerClass
17859      * An additional CSS class used to style the trigger button.  The trigger will always get the
17860      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17861      * which displays a calendar icon).
17862      */
17863     triggerClass : 'x-form-date-trigger',
17864     
17865
17866     /**
17867      * @cfg {Boolean} useIso
17868      * if enabled, then the date field will use a hidden field to store the 
17869      * real value as iso formated date. default (false)
17870      */ 
17871     useIso : false,
17872     /**
17873      * @cfg {String/Object} autoCreate
17874      * A DomHelper element spec, or true for a default element spec (defaults to
17875      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17876      */ 
17877     // private
17878     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17879     
17880     // private
17881     hiddenField: false,
17882     
17883     onRender : function(ct, position)
17884     {
17885         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17886         if (this.useIso) {
17887             //this.el.dom.removeAttribute('name'); 
17888             Roo.log("Changing name?");
17889             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17890             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17891                     'before', true);
17892             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17893             // prevent input submission
17894             this.hiddenName = this.name;
17895         }
17896             
17897             
17898     },
17899     
17900     // private
17901     validateValue : function(value)
17902     {
17903         value = this.formatDate(value);
17904         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17905             Roo.log('super failed');
17906             return false;
17907         }
17908         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17909              return true;
17910         }
17911         var svalue = value;
17912         value = this.parseDate(value);
17913         if(!value){
17914             Roo.log('parse date failed' + svalue);
17915             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17916             return false;
17917         }
17918         var time = value.getTime();
17919         if(this.minValue && time < this.minValue.getTime()){
17920             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17921             return false;
17922         }
17923         if(this.maxValue && time > this.maxValue.getTime()){
17924             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17925             return false;
17926         }
17927         if(this.disabledDays){
17928             var day = value.getDay();
17929             for(var i = 0; i < this.disabledDays.length; i++) {
17930                 if(day === this.disabledDays[i]){
17931                     this.markInvalid(this.disabledDaysText);
17932                     return false;
17933                 }
17934             }
17935         }
17936         var fvalue = this.formatDate(value);
17937         if(this.ddMatch && this.ddMatch.test(fvalue)){
17938             this.markInvalid(String.format(this.disabledDatesText, fvalue));
17939             return false;
17940         }
17941         return true;
17942     },
17943
17944     // private
17945     // Provides logic to override the default TriggerField.validateBlur which just returns true
17946     validateBlur : function(){
17947         return !this.menu || !this.menu.isVisible();
17948     },
17949     
17950     getName: function()
17951     {
17952         // returns hidden if it's set..
17953         if (!this.rendered) {return ''};
17954         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
17955         
17956     },
17957
17958     /**
17959      * Returns the current date value of the date field.
17960      * @return {Date} The date value
17961      */
17962     getValue : function(){
17963         
17964         return  this.hiddenField ?
17965                 this.hiddenField.value :
17966                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17967     },
17968
17969     /**
17970      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
17971      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17972      * (the default format used is "m/d/y").
17973      * <br />Usage:
17974      * <pre><code>
17975 //All of these calls set the same date value (May 4, 2006)
17976
17977 //Pass a date object:
17978 var dt = new Date('5/4/06');
17979 dateField.setValue(dt);
17980
17981 //Pass a date string (default format):
17982 dateField.setValue('5/4/06');
17983
17984 //Pass a date string (custom format):
17985 dateField.format = 'Y-m-d';
17986 dateField.setValue('2006-5-4');
17987 </code></pre>
17988      * @param {String/Date} date The date or valid date string
17989      */
17990     setValue : function(date){
17991         if (this.hiddenField) {
17992             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
17993         }
17994         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
17995         // make sure the value field is always stored as a date..
17996         this.value = this.parseDate(date);
17997         
17998         
17999     },
18000
18001     // private
18002     parseDate : function(value){
18003         if(!value || value instanceof Date){
18004             return value;
18005         }
18006         var v = Date.parseDate(value, this.format);
18007          if (!v && this.useIso) {
18008             v = Date.parseDate(value, 'Y-m-d');
18009         }
18010         if(!v && this.altFormats){
18011             if(!this.altFormatsArray){
18012                 this.altFormatsArray = this.altFormats.split("|");
18013             }
18014             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18015                 v = Date.parseDate(value, this.altFormatsArray[i]);
18016             }
18017         }
18018         return v;
18019     },
18020
18021     // private
18022     formatDate : function(date, fmt){
18023         return (!date || !(date instanceof Date)) ?
18024                date : date.dateFormat(fmt || this.format);
18025     },
18026
18027     // private
18028     menuListeners : {
18029         select: function(m, d){
18030             
18031             this.setValue(d);
18032             this.fireEvent('select', this, d);
18033         },
18034         show : function(){ // retain focus styling
18035             this.onFocus();
18036         },
18037         hide : function(){
18038             this.focus.defer(10, this);
18039             var ml = this.menuListeners;
18040             this.menu.un("select", ml.select,  this);
18041             this.menu.un("show", ml.show,  this);
18042             this.menu.un("hide", ml.hide,  this);
18043         }
18044     },
18045
18046     // private
18047     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18048     onTriggerClick : function(){
18049         if(this.disabled){
18050             return;
18051         }
18052         if(this.menu == null){
18053             this.menu = new Roo.menu.DateMenu();
18054         }
18055         Roo.apply(this.menu.picker,  {
18056             showClear: this.allowBlank,
18057             minDate : this.minValue,
18058             maxDate : this.maxValue,
18059             disabledDatesRE : this.ddMatch,
18060             disabledDatesText : this.disabledDatesText,
18061             disabledDays : this.disabledDays,
18062             disabledDaysText : this.disabledDaysText,
18063             format : this.useIso ? 'Y-m-d' : this.format,
18064             minText : String.format(this.minText, this.formatDate(this.minValue)),
18065             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18066         });
18067         this.menu.on(Roo.apply({}, this.menuListeners, {
18068             scope:this
18069         }));
18070         this.menu.picker.setValue(this.getValue() || new Date());
18071         this.menu.show(this.el, "tl-bl?");
18072     },
18073
18074     beforeBlur : function(){
18075         var v = this.parseDate(this.getRawValue());
18076         if(v){
18077             this.setValue(v);
18078         }
18079     },
18080
18081     /*@
18082      * overide
18083      * 
18084      */
18085     isDirty : function() {
18086         if(this.disabled) {
18087             return false;
18088         }
18089         
18090         if(typeof(this.startValue) === 'undefined'){
18091             return false;
18092         }
18093         
18094         return String(this.getValue()) !== String(this.startValue);
18095         
18096     }
18097 });/*
18098  * Based on:
18099  * Ext JS Library 1.1.1
18100  * Copyright(c) 2006-2007, Ext JS, LLC.
18101  *
18102  * Originally Released Under LGPL - original licence link has changed is not relivant.
18103  *
18104  * Fork - LGPL
18105  * <script type="text/javascript">
18106  */
18107  
18108 /**
18109  * @class Roo.form.MonthField
18110  * @extends Roo.form.TriggerField
18111  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
18112 * @constructor
18113 * Create a new MonthField
18114 * @param {Object} config
18115  */
18116 Roo.form.MonthField = function(config){
18117     
18118     Roo.form.MonthField.superclass.constructor.call(this, config);
18119     
18120       this.addEvents({
18121          
18122         /**
18123          * @event select
18124          * Fires when a date is selected
18125              * @param {Roo.form.MonthFieeld} combo This combo box
18126              * @param {Date} date The date selected
18127              */
18128         'select' : true
18129          
18130     });
18131     
18132     
18133     if(typeof this.minValue == "string") {
18134         this.minValue = this.parseDate(this.minValue);
18135     }
18136     if(typeof this.maxValue == "string") {
18137         this.maxValue = this.parseDate(this.maxValue);
18138     }
18139     this.ddMatch = null;
18140     if(this.disabledDates){
18141         var dd = this.disabledDates;
18142         var re = "(?:";
18143         for(var i = 0; i < dd.length; i++){
18144             re += dd[i];
18145             if(i != dd.length-1) {
18146                 re += "|";
18147             }
18148         }
18149         this.ddMatch = new RegExp(re + ")");
18150     }
18151 };
18152
18153 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
18154     /**
18155      * @cfg {String} format
18156      * The default date format string which can be overriden for localization support.  The format must be
18157      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18158      */
18159     format : "M Y",
18160     /**
18161      * @cfg {String} altFormats
18162      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18163      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18164      */
18165     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
18166     /**
18167      * @cfg {Array} disabledDays
18168      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
18169      */
18170     disabledDays : [0,1,2,3,4,5,6],
18171     /**
18172      * @cfg {String} disabledDaysText
18173      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
18174      */
18175     disabledDaysText : "Disabled",
18176     /**
18177      * @cfg {Array} disabledDates
18178      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
18179      * expression so they are very powerful. Some examples:
18180      * <ul>
18181      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
18182      * <li>["03/08", "09/16"] would disable those days for every year</li>
18183      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
18184      * <li>["03/../2006"] would disable every day in March 2006</li>
18185      * <li>["^03"] would disable every day in every March</li>
18186      * </ul>
18187      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
18188      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
18189      */
18190     disabledDates : null,
18191     /**
18192      * @cfg {String} disabledDatesText
18193      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
18194      */
18195     disabledDatesText : "Disabled",
18196     /**
18197      * @cfg {Date/String} minValue
18198      * The minimum allowed date. Can be either a Javascript date object or a string date in a
18199      * valid format (defaults to null).
18200      */
18201     minValue : null,
18202     /**
18203      * @cfg {Date/String} maxValue
18204      * The maximum allowed date. Can be either a Javascript date object or a string date in a
18205      * valid format (defaults to null).
18206      */
18207     maxValue : null,
18208     /**
18209      * @cfg {String} minText
18210      * The error text to display when the date in the cell is before minValue (defaults to
18211      * 'The date in this field must be after {minValue}').
18212      */
18213     minText : "The date in this field must be equal to or after {0}",
18214     /**
18215      * @cfg {String} maxTextf
18216      * The error text to display when the date in the cell is after maxValue (defaults to
18217      * 'The date in this field must be before {maxValue}').
18218      */
18219     maxText : "The date in this field must be equal to or before {0}",
18220     /**
18221      * @cfg {String} invalidText
18222      * The error text to display when the date in the field is invalid (defaults to
18223      * '{value} is not a valid date - it must be in the format {format}').
18224      */
18225     invalidText : "{0} is not a valid date - it must be in the format {1}",
18226     /**
18227      * @cfg {String} triggerClass
18228      * An additional CSS class used to style the trigger button.  The trigger will always get the
18229      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18230      * which displays a calendar icon).
18231      */
18232     triggerClass : 'x-form-date-trigger',
18233     
18234
18235     /**
18236      * @cfg {Boolean} useIso
18237      * if enabled, then the date field will use a hidden field to store the 
18238      * real value as iso formated date. default (true)
18239      */ 
18240     useIso : true,
18241     /**
18242      * @cfg {String/Object} autoCreate
18243      * A DomHelper element spec, or true for a default element spec (defaults to
18244      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18245      */ 
18246     // private
18247     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18248     
18249     // private
18250     hiddenField: false,
18251     
18252     hideMonthPicker : false,
18253     
18254     onRender : function(ct, position)
18255     {
18256         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18257         if (this.useIso) {
18258             this.el.dom.removeAttribute('name'); 
18259             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18260                     'before', true);
18261             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18262             // prevent input submission
18263             this.hiddenName = this.name;
18264         }
18265             
18266             
18267     },
18268     
18269     // private
18270     validateValue : function(value)
18271     {
18272         value = this.formatDate(value);
18273         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18274             return false;
18275         }
18276         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18277              return true;
18278         }
18279         var svalue = value;
18280         value = this.parseDate(value);
18281         if(!value){
18282             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18283             return false;
18284         }
18285         var time = value.getTime();
18286         if(this.minValue && time < this.minValue.getTime()){
18287             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18288             return false;
18289         }
18290         if(this.maxValue && time > this.maxValue.getTime()){
18291             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18292             return false;
18293         }
18294         /*if(this.disabledDays){
18295             var day = value.getDay();
18296             for(var i = 0; i < this.disabledDays.length; i++) {
18297                 if(day === this.disabledDays[i]){
18298                     this.markInvalid(this.disabledDaysText);
18299                     return false;
18300                 }
18301             }
18302         }
18303         */
18304         var fvalue = this.formatDate(value);
18305         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18306             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18307             return false;
18308         }
18309         */
18310         return true;
18311     },
18312
18313     // private
18314     // Provides logic to override the default TriggerField.validateBlur which just returns true
18315     validateBlur : function(){
18316         return !this.menu || !this.menu.isVisible();
18317     },
18318
18319     /**
18320      * Returns the current date value of the date field.
18321      * @return {Date} The date value
18322      */
18323     getValue : function(){
18324         
18325         
18326         
18327         return  this.hiddenField ?
18328                 this.hiddenField.value :
18329                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18330     },
18331
18332     /**
18333      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18334      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18335      * (the default format used is "m/d/y").
18336      * <br />Usage:
18337      * <pre><code>
18338 //All of these calls set the same date value (May 4, 2006)
18339
18340 //Pass a date object:
18341 var dt = new Date('5/4/06');
18342 monthField.setValue(dt);
18343
18344 //Pass a date string (default format):
18345 monthField.setValue('5/4/06');
18346
18347 //Pass a date string (custom format):
18348 monthField.format = 'Y-m-d';
18349 monthField.setValue('2006-5-4');
18350 </code></pre>
18351      * @param {String/Date} date The date or valid date string
18352      */
18353     setValue : function(date){
18354         Roo.log('month setValue' + date);
18355         // can only be first of month..
18356         
18357         var val = this.parseDate(date);
18358         
18359         if (this.hiddenField) {
18360             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18361         }
18362         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18363         this.value = this.parseDate(date);
18364     },
18365
18366     // private
18367     parseDate : function(value){
18368         if(!value || value instanceof Date){
18369             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18370             return value;
18371         }
18372         var v = Date.parseDate(value, this.format);
18373         if (!v && this.useIso) {
18374             v = Date.parseDate(value, 'Y-m-d');
18375         }
18376         if (v) {
18377             // 
18378             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18379         }
18380         
18381         
18382         if(!v && this.altFormats){
18383             if(!this.altFormatsArray){
18384                 this.altFormatsArray = this.altFormats.split("|");
18385             }
18386             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18387                 v = Date.parseDate(value, this.altFormatsArray[i]);
18388             }
18389         }
18390         return v;
18391     },
18392
18393     // private
18394     formatDate : function(date, fmt){
18395         return (!date || !(date instanceof Date)) ?
18396                date : date.dateFormat(fmt || this.format);
18397     },
18398
18399     // private
18400     menuListeners : {
18401         select: function(m, d){
18402             this.setValue(d);
18403             this.fireEvent('select', this, d);
18404         },
18405         show : function(){ // retain focus styling
18406             this.onFocus();
18407         },
18408         hide : function(){
18409             this.focus.defer(10, this);
18410             var ml = this.menuListeners;
18411             this.menu.un("select", ml.select,  this);
18412             this.menu.un("show", ml.show,  this);
18413             this.menu.un("hide", ml.hide,  this);
18414         }
18415     },
18416     // private
18417     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18418     onTriggerClick : function(){
18419         if(this.disabled){
18420             return;
18421         }
18422         if(this.menu == null){
18423             this.menu = new Roo.menu.DateMenu();
18424            
18425         }
18426         
18427         Roo.apply(this.menu.picker,  {
18428             
18429             showClear: this.allowBlank,
18430             minDate : this.minValue,
18431             maxDate : this.maxValue,
18432             disabledDatesRE : this.ddMatch,
18433             disabledDatesText : this.disabledDatesText,
18434             
18435             format : this.useIso ? 'Y-m-d' : this.format,
18436             minText : String.format(this.minText, this.formatDate(this.minValue)),
18437             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18438             
18439         });
18440          this.menu.on(Roo.apply({}, this.menuListeners, {
18441             scope:this
18442         }));
18443        
18444         
18445         var m = this.menu;
18446         var p = m.picker;
18447         
18448         // hide month picker get's called when we called by 'before hide';
18449         
18450         var ignorehide = true;
18451         p.hideMonthPicker  = function(disableAnim){
18452             if (ignorehide) {
18453                 return;
18454             }
18455              if(this.monthPicker){
18456                 Roo.log("hideMonthPicker called");
18457                 if(disableAnim === true){
18458                     this.monthPicker.hide();
18459                 }else{
18460                     this.monthPicker.slideOut('t', {duration:.2});
18461                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18462                     p.fireEvent("select", this, this.value);
18463                     m.hide();
18464                 }
18465             }
18466         }
18467         
18468         Roo.log('picker set value');
18469         Roo.log(this.getValue());
18470         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18471         m.show(this.el, 'tl-bl?');
18472         ignorehide  = false;
18473         // this will trigger hideMonthPicker..
18474         
18475         
18476         // hidden the day picker
18477         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18478         
18479         
18480         
18481       
18482         
18483         p.showMonthPicker.defer(100, p);
18484     
18485         
18486        
18487     },
18488
18489     beforeBlur : function(){
18490         var v = this.parseDate(this.getRawValue());
18491         if(v){
18492             this.setValue(v);
18493         }
18494     }
18495
18496     /** @cfg {Boolean} grow @hide */
18497     /** @cfg {Number} growMin @hide */
18498     /** @cfg {Number} growMax @hide */
18499     /**
18500      * @hide
18501      * @method autoSize
18502      */
18503 });/*
18504  * Based on:
18505  * Ext JS Library 1.1.1
18506  * Copyright(c) 2006-2007, Ext JS, LLC.
18507  *
18508  * Originally Released Under LGPL - original licence link has changed is not relivant.
18509  *
18510  * Fork - LGPL
18511  * <script type="text/javascript">
18512  */
18513  
18514
18515 /**
18516  * @class Roo.form.ComboBox
18517  * @extends Roo.form.TriggerField
18518  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18519  * @constructor
18520  * Create a new ComboBox.
18521  * @param {Object} config Configuration options
18522  */
18523 Roo.form.ComboBox = function(config){
18524     Roo.form.ComboBox.superclass.constructor.call(this, config);
18525     this.addEvents({
18526         /**
18527          * @event expand
18528          * Fires when the dropdown list is expanded
18529              * @param {Roo.form.ComboBox} combo This combo box
18530              */
18531         'expand' : true,
18532         /**
18533          * @event collapse
18534          * Fires when the dropdown list is collapsed
18535              * @param {Roo.form.ComboBox} combo This combo box
18536              */
18537         'collapse' : true,
18538         /**
18539          * @event beforeselect
18540          * Fires before a list item is selected. Return false to cancel the selection.
18541              * @param {Roo.form.ComboBox} combo This combo box
18542              * @param {Roo.data.Record} record The data record returned from the underlying store
18543              * @param {Number} index The index of the selected item in the dropdown list
18544              */
18545         'beforeselect' : true,
18546         /**
18547          * @event select
18548          * Fires when a list item is selected
18549              * @param {Roo.form.ComboBox} combo This combo box
18550              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18551              * @param {Number} index The index of the selected item in the dropdown list
18552              */
18553         'select' : true,
18554         /**
18555          * @event beforequery
18556          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18557          * The event object passed has these properties:
18558              * @param {Roo.form.ComboBox} combo This combo box
18559              * @param {String} query The query
18560              * @param {Boolean} forceAll true to force "all" query
18561              * @param {Boolean} cancel true to cancel the query
18562              * @param {Object} e The query event object
18563              */
18564         'beforequery': true,
18565          /**
18566          * @event add
18567          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18568              * @param {Roo.form.ComboBox} combo This combo box
18569              */
18570         'add' : true,
18571         /**
18572          * @event edit
18573          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18574              * @param {Roo.form.ComboBox} combo This combo box
18575              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18576              */
18577         'edit' : true
18578         
18579         
18580     });
18581     if(this.transform){
18582         this.allowDomMove = false;
18583         var s = Roo.getDom(this.transform);
18584         if(!this.hiddenName){
18585             this.hiddenName = s.name;
18586         }
18587         if(!this.store){
18588             this.mode = 'local';
18589             var d = [], opts = s.options;
18590             for(var i = 0, len = opts.length;i < len; i++){
18591                 var o = opts[i];
18592                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18593                 if(o.selected) {
18594                     this.value = value;
18595                 }
18596                 d.push([value, o.text]);
18597             }
18598             this.store = new Roo.data.SimpleStore({
18599                 'id': 0,
18600                 fields: ['value', 'text'],
18601                 data : d
18602             });
18603             this.valueField = 'value';
18604             this.displayField = 'text';
18605         }
18606         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18607         if(!this.lazyRender){
18608             this.target = true;
18609             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18610             s.parentNode.removeChild(s); // remove it
18611             this.render(this.el.parentNode);
18612         }else{
18613             s.parentNode.removeChild(s); // remove it
18614         }
18615
18616     }
18617     if (this.store) {
18618         this.store = Roo.factory(this.store, Roo.data);
18619     }
18620     
18621     this.selectedIndex = -1;
18622     if(this.mode == 'local'){
18623         if(config.queryDelay === undefined){
18624             this.queryDelay = 10;
18625         }
18626         if(config.minChars === undefined){
18627             this.minChars = 0;
18628         }
18629     }
18630 };
18631
18632 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18633     /**
18634      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18635      */
18636     /**
18637      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18638      * rendering into an Roo.Editor, defaults to false)
18639      */
18640     /**
18641      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18642      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18643      */
18644     /**
18645      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18646      */
18647     /**
18648      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18649      * the dropdown list (defaults to undefined, with no header element)
18650      */
18651
18652      /**
18653      * @cfg {String/Roo.Template} tpl The template to use to render the output
18654      */
18655      
18656     // private
18657     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18658     /**
18659      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18660      */
18661     listWidth: undefined,
18662     /**
18663      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18664      * mode = 'remote' or 'text' if mode = 'local')
18665      */
18666     displayField: undefined,
18667     /**
18668      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18669      * mode = 'remote' or 'value' if mode = 'local'). 
18670      * Note: use of a valueField requires the user make a selection
18671      * in order for a value to be mapped.
18672      */
18673     valueField: undefined,
18674     
18675     
18676     /**
18677      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18678      * field's data value (defaults to the underlying DOM element's name)
18679      */
18680     hiddenName: undefined,
18681     /**
18682      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18683      */
18684     listClass: '',
18685     /**
18686      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18687      */
18688     selectedClass: 'x-combo-selected',
18689     /**
18690      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18691      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18692      * which displays a downward arrow icon).
18693      */
18694     triggerClass : 'x-form-arrow-trigger',
18695     /**
18696      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18697      */
18698     shadow:'sides',
18699     /**
18700      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18701      * anchor positions (defaults to 'tl-bl')
18702      */
18703     listAlign: 'tl-bl?',
18704     /**
18705      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18706      */
18707     maxHeight: 300,
18708     /**
18709      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18710      * query specified by the allQuery config option (defaults to 'query')
18711      */
18712     triggerAction: 'query',
18713     /**
18714      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18715      * (defaults to 4, does not apply if editable = false)
18716      */
18717     minChars : 4,
18718     /**
18719      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18720      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18721      */
18722     typeAhead: false,
18723     /**
18724      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18725      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18726      */
18727     queryDelay: 500,
18728     /**
18729      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18730      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18731      */
18732     pageSize: 0,
18733     /**
18734      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18735      * when editable = true (defaults to false)
18736      */
18737     selectOnFocus:false,
18738     /**
18739      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18740      */
18741     queryParam: 'query',
18742     /**
18743      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18744      * when mode = 'remote' (defaults to 'Loading...')
18745      */
18746     loadingText: 'Loading...',
18747     /**
18748      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18749      */
18750     resizable: false,
18751     /**
18752      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18753      */
18754     handleHeight : 8,
18755     /**
18756      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18757      * traditional select (defaults to true)
18758      */
18759     editable: true,
18760     /**
18761      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18762      */
18763     allQuery: '',
18764     /**
18765      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18766      */
18767     mode: 'remote',
18768     /**
18769      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18770      * listWidth has a higher value)
18771      */
18772     minListWidth : 70,
18773     /**
18774      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18775      * allow the user to set arbitrary text into the field (defaults to false)
18776      */
18777     forceSelection:false,
18778     /**
18779      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18780      * if typeAhead = true (defaults to 250)
18781      */
18782     typeAheadDelay : 250,
18783     /**
18784      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18785      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18786      */
18787     valueNotFoundText : undefined,
18788     /**
18789      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18790      */
18791     blockFocus : false,
18792     
18793     /**
18794      * @cfg {Boolean} disableClear Disable showing of clear button.
18795      */
18796     disableClear : false,
18797     /**
18798      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18799      */
18800     alwaysQuery : false,
18801     
18802     //private
18803     addicon : false,
18804     editicon: false,
18805     
18806     // element that contains real text value.. (when hidden is used..)
18807      
18808     // private
18809     onRender : function(ct, position){
18810         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18811         if(this.hiddenName){
18812             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18813                     'before', true);
18814             this.hiddenField.value =
18815                 this.hiddenValue !== undefined ? this.hiddenValue :
18816                 this.value !== undefined ? this.value : '';
18817
18818             // prevent input submission
18819             this.el.dom.removeAttribute('name');
18820              
18821              
18822         }
18823         if(Roo.isGecko){
18824             this.el.dom.setAttribute('autocomplete', 'off');
18825         }
18826
18827         var cls = 'x-combo-list';
18828
18829         this.list = new Roo.Layer({
18830             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18831         });
18832
18833         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18834         this.list.setWidth(lw);
18835         this.list.swallowEvent('mousewheel');
18836         this.assetHeight = 0;
18837
18838         if(this.title){
18839             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18840             this.assetHeight += this.header.getHeight();
18841         }
18842
18843         this.innerList = this.list.createChild({cls:cls+'-inner'});
18844         this.innerList.on('mouseover', this.onViewOver, this);
18845         this.innerList.on('mousemove', this.onViewMove, this);
18846         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18847         
18848         if(this.allowBlank && !this.pageSize && !this.disableClear){
18849             this.footer = this.list.createChild({cls:cls+'-ft'});
18850             this.pageTb = new Roo.Toolbar(this.footer);
18851            
18852         }
18853         if(this.pageSize){
18854             this.footer = this.list.createChild({cls:cls+'-ft'});
18855             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18856                     {pageSize: this.pageSize});
18857             
18858         }
18859         
18860         if (this.pageTb && this.allowBlank && !this.disableClear) {
18861             var _this = this;
18862             this.pageTb.add(new Roo.Toolbar.Fill(), {
18863                 cls: 'x-btn-icon x-btn-clear',
18864                 text: '&#160;',
18865                 handler: function()
18866                 {
18867                     _this.collapse();
18868                     _this.clearValue();
18869                     _this.onSelect(false, -1);
18870                 }
18871             });
18872         }
18873         if (this.footer) {
18874             this.assetHeight += this.footer.getHeight();
18875         }
18876         
18877
18878         if(!this.tpl){
18879             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18880         }
18881
18882         this.view = new Roo.View(this.innerList, this.tpl, {
18883             singleSelect:true, store: this.store, selectedClass: this.selectedClass
18884         });
18885
18886         this.view.on('click', this.onViewClick, this);
18887
18888         this.store.on('beforeload', this.onBeforeLoad, this);
18889         this.store.on('load', this.onLoad, this);
18890         this.store.on('loadexception', this.onLoadException, this);
18891
18892         if(this.resizable){
18893             this.resizer = new Roo.Resizable(this.list,  {
18894                pinned:true, handles:'se'
18895             });
18896             this.resizer.on('resize', function(r, w, h){
18897                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18898                 this.listWidth = w;
18899                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18900                 this.restrictHeight();
18901             }, this);
18902             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18903         }
18904         if(!this.editable){
18905             this.editable = true;
18906             this.setEditable(false);
18907         }  
18908         
18909         
18910         if (typeof(this.events.add.listeners) != 'undefined') {
18911             
18912             this.addicon = this.wrap.createChild(
18913                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
18914        
18915             this.addicon.on('click', function(e) {
18916                 this.fireEvent('add', this);
18917             }, this);
18918         }
18919         if (typeof(this.events.edit.listeners) != 'undefined') {
18920             
18921             this.editicon = this.wrap.createChild(
18922                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
18923             if (this.addicon) {
18924                 this.editicon.setStyle('margin-left', '40px');
18925             }
18926             this.editicon.on('click', function(e) {
18927                 
18928                 // we fire even  if inothing is selected..
18929                 this.fireEvent('edit', this, this.lastData );
18930                 
18931             }, this);
18932         }
18933         
18934         
18935         
18936     },
18937
18938     // private
18939     initEvents : function(){
18940         Roo.form.ComboBox.superclass.initEvents.call(this);
18941
18942         this.keyNav = new Roo.KeyNav(this.el, {
18943             "up" : function(e){
18944                 this.inKeyMode = true;
18945                 this.selectPrev();
18946             },
18947
18948             "down" : function(e){
18949                 if(!this.isExpanded()){
18950                     this.onTriggerClick();
18951                 }else{
18952                     this.inKeyMode = true;
18953                     this.selectNext();
18954                 }
18955             },
18956
18957             "enter" : function(e){
18958                 this.onViewClick();
18959                 //return true;
18960             },
18961
18962             "esc" : function(e){
18963                 this.collapse();
18964             },
18965
18966             "tab" : function(e){
18967                 this.onViewClick(false);
18968                 this.fireEvent("specialkey", this, e);
18969                 return true;
18970             },
18971
18972             scope : this,
18973
18974             doRelay : function(foo, bar, hname){
18975                 if(hname == 'down' || this.scope.isExpanded()){
18976                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18977                 }
18978                 return true;
18979             },
18980
18981             forceKeyDown: true
18982         });
18983         this.queryDelay = Math.max(this.queryDelay || 10,
18984                 this.mode == 'local' ? 10 : 250);
18985         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18986         if(this.typeAhead){
18987             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18988         }
18989         if(this.editable !== false){
18990             this.el.on("keyup", this.onKeyUp, this);
18991         }
18992         if(this.forceSelection){
18993             this.on('blur', this.doForce, this);
18994         }
18995     },
18996
18997     onDestroy : function(){
18998         if(this.view){
18999             this.view.setStore(null);
19000             this.view.el.removeAllListeners();
19001             this.view.el.remove();
19002             this.view.purgeListeners();
19003         }
19004         if(this.list){
19005             this.list.destroy();
19006         }
19007         if(this.store){
19008             this.store.un('beforeload', this.onBeforeLoad, this);
19009             this.store.un('load', this.onLoad, this);
19010             this.store.un('loadexception', this.onLoadException, this);
19011         }
19012         Roo.form.ComboBox.superclass.onDestroy.call(this);
19013     },
19014
19015     // private
19016     fireKey : function(e){
19017         if(e.isNavKeyPress() && !this.list.isVisible()){
19018             this.fireEvent("specialkey", this, e);
19019         }
19020     },
19021
19022     // private
19023     onResize: function(w, h){
19024         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
19025         
19026         if(typeof w != 'number'){
19027             // we do not handle it!?!?
19028             return;
19029         }
19030         var tw = this.trigger.getWidth();
19031         tw += this.addicon ? this.addicon.getWidth() : 0;
19032         tw += this.editicon ? this.editicon.getWidth() : 0;
19033         var x = w - tw;
19034         this.el.setWidth( this.adjustWidth('input', x));
19035             
19036         this.trigger.setStyle('left', x+'px');
19037         
19038         if(this.list && this.listWidth === undefined){
19039             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
19040             this.list.setWidth(lw);
19041             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19042         }
19043         
19044     
19045         
19046     },
19047
19048     /**
19049      * Allow or prevent the user from directly editing the field text.  If false is passed,
19050      * the user will only be able to select from the items defined in the dropdown list.  This method
19051      * is the runtime equivalent of setting the 'editable' config option at config time.
19052      * @param {Boolean} value True to allow the user to directly edit the field text
19053      */
19054     setEditable : function(value){
19055         if(value == this.editable){
19056             return;
19057         }
19058         this.editable = value;
19059         if(!value){
19060             this.el.dom.setAttribute('readOnly', true);
19061             this.el.on('mousedown', this.onTriggerClick,  this);
19062             this.el.addClass('x-combo-noedit');
19063         }else{
19064             this.el.dom.setAttribute('readOnly', false);
19065             this.el.un('mousedown', this.onTriggerClick,  this);
19066             this.el.removeClass('x-combo-noedit');
19067         }
19068     },
19069
19070     // private
19071     onBeforeLoad : function(){
19072         if(!this.hasFocus){
19073             return;
19074         }
19075         this.innerList.update(this.loadingText ?
19076                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
19077         this.restrictHeight();
19078         this.selectedIndex = -1;
19079     },
19080
19081     // private
19082     onLoad : function(){
19083         if(!this.hasFocus){
19084             return;
19085         }
19086         if(this.store.getCount() > 0){
19087             this.expand();
19088             this.restrictHeight();
19089             if(this.lastQuery == this.allQuery){
19090                 if(this.editable){
19091                     this.el.dom.select();
19092                 }
19093                 if(!this.selectByValue(this.value, true)){
19094                     this.select(0, true);
19095                 }
19096             }else{
19097                 this.selectNext();
19098                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
19099                     this.taTask.delay(this.typeAheadDelay);
19100                 }
19101             }
19102         }else{
19103             this.onEmptyResults();
19104         }
19105         //this.el.focus();
19106     },
19107     // private
19108     onLoadException : function()
19109     {
19110         this.collapse();
19111         Roo.log(this.store.reader.jsonData);
19112         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
19113             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
19114         }
19115         
19116         
19117     },
19118     // private
19119     onTypeAhead : function(){
19120         if(this.store.getCount() > 0){
19121             var r = this.store.getAt(0);
19122             var newValue = r.data[this.displayField];
19123             var len = newValue.length;
19124             var selStart = this.getRawValue().length;
19125             if(selStart != len){
19126                 this.setRawValue(newValue);
19127                 this.selectText(selStart, newValue.length);
19128             }
19129         }
19130     },
19131
19132     // private
19133     onSelect : function(record, index){
19134         if(this.fireEvent('beforeselect', this, record, index) !== false){
19135             this.setFromData(index > -1 ? record.data : false);
19136             this.collapse();
19137             this.fireEvent('select', this, record, index);
19138         }
19139     },
19140
19141     /**
19142      * Returns the currently selected field value or empty string if no value is set.
19143      * @return {String} value The selected value
19144      */
19145     getValue : function(){
19146         if(this.valueField){
19147             return typeof this.value != 'undefined' ? this.value : '';
19148         }
19149         return Roo.form.ComboBox.superclass.getValue.call(this);
19150     },
19151
19152     /**
19153      * Clears any text/value currently set in the field
19154      */
19155     clearValue : function(){
19156         if(this.hiddenField){
19157             this.hiddenField.value = '';
19158         }
19159         this.value = '';
19160         this.setRawValue('');
19161         this.lastSelectionText = '';
19162         
19163     },
19164
19165     /**
19166      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
19167      * will be displayed in the field.  If the value does not match the data value of an existing item,
19168      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
19169      * Otherwise the field will be blank (although the value will still be set).
19170      * @param {String} value The value to match
19171      */
19172     setValue : function(v){
19173         var text = v;
19174         if(this.valueField){
19175             var r = this.findRecord(this.valueField, v);
19176             if(r){
19177                 text = r.data[this.displayField];
19178             }else if(this.valueNotFoundText !== undefined){
19179                 text = this.valueNotFoundText;
19180             }
19181         }
19182         this.lastSelectionText = text;
19183         if(this.hiddenField){
19184             this.hiddenField.value = v;
19185         }
19186         Roo.form.ComboBox.superclass.setValue.call(this, text);
19187         this.value = v;
19188     },
19189     /**
19190      * @property {Object} the last set data for the element
19191      */
19192     
19193     lastData : false,
19194     /**
19195      * Sets the value of the field based on a object which is related to the record format for the store.
19196      * @param {Object} value the value to set as. or false on reset?
19197      */
19198     setFromData : function(o){
19199         var dv = ''; // display value
19200         var vv = ''; // value value..
19201         this.lastData = o;
19202         if (this.displayField) {
19203             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
19204         } else {
19205             // this is an error condition!!!
19206             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
19207         }
19208         
19209         if(this.valueField){
19210             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
19211         }
19212         if(this.hiddenField){
19213             this.hiddenField.value = vv;
19214             
19215             this.lastSelectionText = dv;
19216             Roo.form.ComboBox.superclass.setValue.call(this, dv);
19217             this.value = vv;
19218             return;
19219         }
19220         // no hidden field.. - we store the value in 'value', but still display
19221         // display field!!!!
19222         this.lastSelectionText = dv;
19223         Roo.form.ComboBox.superclass.setValue.call(this, dv);
19224         this.value = vv;
19225         
19226         
19227     },
19228     // private
19229     reset : function(){
19230         // overridden so that last data is reset..
19231         this.setValue(this.resetValue);
19232         this.originalValue = this.getValue();
19233         this.clearInvalid();
19234         this.lastData = false;
19235         if (this.view) {
19236             this.view.clearSelections();
19237         }
19238     },
19239     // private
19240     findRecord : function(prop, value){
19241         var record;
19242         if(this.store.getCount() > 0){
19243             this.store.each(function(r){
19244                 if(r.data[prop] == value){
19245                     record = r;
19246                     return false;
19247                 }
19248                 return true;
19249             });
19250         }
19251         return record;
19252     },
19253     
19254     getName: function()
19255     {
19256         // returns hidden if it's set..
19257         if (!this.rendered) {return ''};
19258         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
19259         
19260     },
19261     // private
19262     onViewMove : function(e, t){
19263         this.inKeyMode = false;
19264     },
19265
19266     // private
19267     onViewOver : function(e, t){
19268         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19269             return;
19270         }
19271         var item = this.view.findItemFromChild(t);
19272         if(item){
19273             var index = this.view.indexOf(item);
19274             this.select(index, false);
19275         }
19276     },
19277
19278     // private
19279     onViewClick : function(doFocus)
19280     {
19281         var index = this.view.getSelectedIndexes()[0];
19282         var r = this.store.getAt(index);
19283         if(r){
19284             this.onSelect(r, index);
19285         }
19286         if(doFocus !== false && !this.blockFocus){
19287             this.el.focus();
19288         }
19289     },
19290
19291     // private
19292     restrictHeight : function(){
19293         this.innerList.dom.style.height = '';
19294         var inner = this.innerList.dom;
19295         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19296         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19297         this.list.beginUpdate();
19298         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19299         this.list.alignTo(this.el, this.listAlign);
19300         this.list.endUpdate();
19301     },
19302
19303     // private
19304     onEmptyResults : function(){
19305         this.collapse();
19306     },
19307
19308     /**
19309      * Returns true if the dropdown list is expanded, else false.
19310      */
19311     isExpanded : function(){
19312         return this.list.isVisible();
19313     },
19314
19315     /**
19316      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19317      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19318      * @param {String} value The data value of the item to select
19319      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19320      * selected item if it is not currently in view (defaults to true)
19321      * @return {Boolean} True if the value matched an item in the list, else false
19322      */
19323     selectByValue : function(v, scrollIntoView){
19324         if(v !== undefined && v !== null){
19325             var r = this.findRecord(this.valueField || this.displayField, v);
19326             if(r){
19327                 this.select(this.store.indexOf(r), scrollIntoView);
19328                 return true;
19329             }
19330         }
19331         return false;
19332     },
19333
19334     /**
19335      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19336      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19337      * @param {Number} index The zero-based index of the list item to select
19338      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19339      * selected item if it is not currently in view (defaults to true)
19340      */
19341     select : function(index, scrollIntoView){
19342         this.selectedIndex = index;
19343         this.view.select(index);
19344         if(scrollIntoView !== false){
19345             var el = this.view.getNode(index);
19346             if(el){
19347                 this.innerList.scrollChildIntoView(el, false);
19348             }
19349         }
19350     },
19351
19352     // private
19353     selectNext : function(){
19354         var ct = this.store.getCount();
19355         if(ct > 0){
19356             if(this.selectedIndex == -1){
19357                 this.select(0);
19358             }else if(this.selectedIndex < ct-1){
19359                 this.select(this.selectedIndex+1);
19360             }
19361         }
19362     },
19363
19364     // private
19365     selectPrev : function(){
19366         var ct = this.store.getCount();
19367         if(ct > 0){
19368             if(this.selectedIndex == -1){
19369                 this.select(0);
19370             }else if(this.selectedIndex != 0){
19371                 this.select(this.selectedIndex-1);
19372             }
19373         }
19374     },
19375
19376     // private
19377     onKeyUp : function(e){
19378         if(this.editable !== false && !e.isSpecialKey()){
19379             this.lastKey = e.getKey();
19380             this.dqTask.delay(this.queryDelay);
19381         }
19382     },
19383
19384     // private
19385     validateBlur : function(){
19386         return !this.list || !this.list.isVisible();   
19387     },
19388
19389     // private
19390     initQuery : function(){
19391         this.doQuery(this.getRawValue());
19392     },
19393
19394     // private
19395     doForce : function(){
19396         if(this.el.dom.value.length > 0){
19397             this.el.dom.value =
19398                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19399              
19400         }
19401     },
19402
19403     /**
19404      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19405      * query allowing the query action to be canceled if needed.
19406      * @param {String} query The SQL query to execute
19407      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19408      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19409      * saved in the current store (defaults to false)
19410      */
19411     doQuery : function(q, forceAll){
19412         if(q === undefined || q === null){
19413             q = '';
19414         }
19415         var qe = {
19416             query: q,
19417             forceAll: forceAll,
19418             combo: this,
19419             cancel:false
19420         };
19421         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19422             return false;
19423         }
19424         q = qe.query;
19425         forceAll = qe.forceAll;
19426         if(forceAll === true || (q.length >= this.minChars)){
19427             if(this.lastQuery != q || this.alwaysQuery){
19428                 this.lastQuery = q;
19429                 if(this.mode == 'local'){
19430                     this.selectedIndex = -1;
19431                     if(forceAll){
19432                         this.store.clearFilter();
19433                     }else{
19434                         this.store.filter(this.displayField, q);
19435                     }
19436                     this.onLoad();
19437                 }else{
19438                     this.store.baseParams[this.queryParam] = q;
19439                     this.store.load({
19440                         params: this.getParams(q)
19441                     });
19442                     this.expand();
19443                 }
19444             }else{
19445                 this.selectedIndex = -1;
19446                 this.onLoad();   
19447             }
19448         }
19449     },
19450
19451     // private
19452     getParams : function(q){
19453         var p = {};
19454         //p[this.queryParam] = q;
19455         if(this.pageSize){
19456             p.start = 0;
19457             p.limit = this.pageSize;
19458         }
19459         return p;
19460     },
19461
19462     /**
19463      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19464      */
19465     collapse : function(){
19466         if(!this.isExpanded()){
19467             return;
19468         }
19469         this.list.hide();
19470         Roo.get(document).un('mousedown', this.collapseIf, this);
19471         Roo.get(document).un('mousewheel', this.collapseIf, this);
19472         if (!this.editable) {
19473             Roo.get(document).un('keydown', this.listKeyPress, this);
19474         }
19475         this.fireEvent('collapse', this);
19476     },
19477
19478     // private
19479     collapseIf : function(e){
19480         if(!e.within(this.wrap) && !e.within(this.list)){
19481             this.collapse();
19482         }
19483     },
19484
19485     /**
19486      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19487      */
19488     expand : function(){
19489         if(this.isExpanded() || !this.hasFocus){
19490             return;
19491         }
19492         this.list.alignTo(this.el, this.listAlign);
19493         this.list.show();
19494         Roo.get(document).on('mousedown', this.collapseIf, this);
19495         Roo.get(document).on('mousewheel', this.collapseIf, this);
19496         if (!this.editable) {
19497             Roo.get(document).on('keydown', this.listKeyPress, this);
19498         }
19499         
19500         this.fireEvent('expand', this);
19501     },
19502
19503     // private
19504     // Implements the default empty TriggerField.onTriggerClick function
19505     onTriggerClick : function(){
19506         if(this.disabled){
19507             return;
19508         }
19509         if(this.isExpanded()){
19510             this.collapse();
19511             if (!this.blockFocus) {
19512                 this.el.focus();
19513             }
19514             
19515         }else {
19516             this.hasFocus = true;
19517             if(this.triggerAction == 'all') {
19518                 this.doQuery(this.allQuery, true);
19519             } else {
19520                 this.doQuery(this.getRawValue());
19521             }
19522             if (!this.blockFocus) {
19523                 this.el.focus();
19524             }
19525         }
19526     },
19527     listKeyPress : function(e)
19528     {
19529         //Roo.log('listkeypress');
19530         // scroll to first matching element based on key pres..
19531         if (e.isSpecialKey()) {
19532             return false;
19533         }
19534         var k = String.fromCharCode(e.getKey()).toUpperCase();
19535         //Roo.log(k);
19536         var match  = false;
19537         var csel = this.view.getSelectedNodes();
19538         var cselitem = false;
19539         if (csel.length) {
19540             var ix = this.view.indexOf(csel[0]);
19541             cselitem  = this.store.getAt(ix);
19542             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19543                 cselitem = false;
19544             }
19545             
19546         }
19547         
19548         this.store.each(function(v) { 
19549             if (cselitem) {
19550                 // start at existing selection.
19551                 if (cselitem.id == v.id) {
19552                     cselitem = false;
19553                 }
19554                 return;
19555             }
19556                 
19557             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19558                 match = this.store.indexOf(v);
19559                 return false;
19560             }
19561         }, this);
19562         
19563         if (match === false) {
19564             return true; // no more action?
19565         }
19566         // scroll to?
19567         this.view.select(match);
19568         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19569         sn.scrollIntoView(sn.dom.parentNode, false);
19570     }
19571
19572     /** 
19573     * @cfg {Boolean} grow 
19574     * @hide 
19575     */
19576     /** 
19577     * @cfg {Number} growMin 
19578     * @hide 
19579     */
19580     /** 
19581     * @cfg {Number} growMax 
19582     * @hide 
19583     */
19584     /**
19585      * @hide
19586      * @method autoSize
19587      */
19588 });/*
19589  * Copyright(c) 2010-2012, Roo J Solutions Limited
19590  *
19591  * Licence LGPL
19592  *
19593  */
19594
19595 /**
19596  * @class Roo.form.ComboBoxArray
19597  * @extends Roo.form.TextField
19598  * A facebook style adder... for lists of email / people / countries  etc...
19599  * pick multiple items from a combo box, and shows each one.
19600  *
19601  *  Fred [x]  Brian [x]  [Pick another |v]
19602  *
19603  *
19604  *  For this to work: it needs various extra information
19605  *    - normal combo problay has
19606  *      name, hiddenName
19607  *    + displayField, valueField
19608  *
19609  *    For our purpose...
19610  *
19611  *
19612  *   If we change from 'extends' to wrapping...
19613  *   
19614  *  
19615  *
19616  
19617  
19618  * @constructor
19619  * Create a new ComboBoxArray.
19620  * @param {Object} config Configuration options
19621  */
19622  
19623
19624 Roo.form.ComboBoxArray = function(config)
19625 {
19626     this.addEvents({
19627         /**
19628          * @event beforeremove
19629          * Fires before remove the value from the list
19630              * @param {Roo.form.ComboBoxArray} _self This combo box array
19631              * @param {Roo.form.ComboBoxArray.Item} item removed item
19632              */
19633         'beforeremove' : true,
19634         /**
19635          * @event remove
19636          * Fires when remove the value from the list
19637              * @param {Roo.form.ComboBoxArray} _self This combo box array
19638              * @param {Roo.form.ComboBoxArray.Item} item removed item
19639              */
19640         'remove' : true
19641         
19642         
19643     });
19644     
19645     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19646     
19647     this.items = new Roo.util.MixedCollection(false);
19648     
19649     // construct the child combo...
19650     
19651     
19652     
19653     
19654    
19655     
19656 }
19657
19658  
19659 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19660
19661     /**
19662      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
19663      */
19664     
19665     lastData : false,
19666     
19667     // behavies liek a hiddne field
19668     inputType:      'hidden',
19669     /**
19670      * @cfg {Number} width The width of the box that displays the selected element
19671      */ 
19672     width:          300,
19673
19674     
19675     
19676     /**
19677      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19678      */
19679     name : false,
19680     /**
19681      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19682      */
19683     hiddenName : false,
19684     
19685     
19686     // private the array of items that are displayed..
19687     items  : false,
19688     // private - the hidden field el.
19689     hiddenEl : false,
19690     // private - the filed el..
19691     el : false,
19692     
19693     //validateValue : function() { return true; }, // all values are ok!
19694     //onAddClick: function() { },
19695     
19696     onRender : function(ct, position) 
19697     {
19698         
19699         // create the standard hidden element
19700         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19701         
19702         
19703         // give fake names to child combo;
19704         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19705         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
19706         
19707         this.combo = Roo.factory(this.combo, Roo.form);
19708         this.combo.onRender(ct, position);
19709         if (typeof(this.combo.width) != 'undefined') {
19710             this.combo.onResize(this.combo.width,0);
19711         }
19712         
19713         this.combo.initEvents();
19714         
19715         // assigned so form know we need to do this..
19716         this.store          = this.combo.store;
19717         this.valueField     = this.combo.valueField;
19718         this.displayField   = this.combo.displayField ;
19719         
19720         
19721         this.combo.wrap.addClass('x-cbarray-grp');
19722         
19723         var cbwrap = this.combo.wrap.createChild(
19724             {tag: 'div', cls: 'x-cbarray-cb'},
19725             this.combo.el.dom
19726         );
19727         
19728              
19729         this.hiddenEl = this.combo.wrap.createChild({
19730             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19731         });
19732         this.el = this.combo.wrap.createChild({
19733             tag: 'input',  type:'hidden' , name: this.name, value : ''
19734         });
19735          //   this.el.dom.removeAttribute("name");
19736         
19737         
19738         this.outerWrap = this.combo.wrap;
19739         this.wrap = cbwrap;
19740         
19741         this.outerWrap.setWidth(this.width);
19742         this.outerWrap.dom.removeChild(this.el.dom);
19743         
19744         this.wrap.dom.appendChild(this.el.dom);
19745         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19746         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19747         
19748         this.combo.trigger.setStyle('position','relative');
19749         this.combo.trigger.setStyle('left', '0px');
19750         this.combo.trigger.setStyle('top', '2px');
19751         
19752         this.combo.el.setStyle('vertical-align', 'text-bottom');
19753         
19754         //this.trigger.setStyle('vertical-align', 'top');
19755         
19756         // this should use the code from combo really... on('add' ....)
19757         if (this.adder) {
19758             
19759         
19760             this.adder = this.outerWrap.createChild(
19761                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19762             var _t = this;
19763             this.adder.on('click', function(e) {
19764                 _t.fireEvent('adderclick', this, e);
19765             }, _t);
19766         }
19767         //var _t = this;
19768         //this.adder.on('click', this.onAddClick, _t);
19769         
19770         
19771         this.combo.on('select', function(cb, rec, ix) {
19772             this.addItem(rec.data);
19773             
19774             cb.setValue('');
19775             cb.el.dom.value = '';
19776             //cb.lastData = rec.data;
19777             // add to list
19778             
19779         }, this);
19780         
19781         
19782     },
19783     
19784     
19785     getName: function()
19786     {
19787         // returns hidden if it's set..
19788         if (!this.rendered) {return ''};
19789         return  this.hiddenName ? this.hiddenName : this.name;
19790         
19791     },
19792     
19793     
19794     onResize: function(w, h){
19795         
19796         return;
19797         // not sure if this is needed..
19798         //this.combo.onResize(w,h);
19799         
19800         if(typeof w != 'number'){
19801             // we do not handle it!?!?
19802             return;
19803         }
19804         var tw = this.combo.trigger.getWidth();
19805         tw += this.addicon ? this.addicon.getWidth() : 0;
19806         tw += this.editicon ? this.editicon.getWidth() : 0;
19807         var x = w - tw;
19808         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19809             
19810         this.combo.trigger.setStyle('left', '0px');
19811         
19812         if(this.list && this.listWidth === undefined){
19813             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19814             this.list.setWidth(lw);
19815             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19816         }
19817         
19818     
19819         
19820     },
19821     
19822     addItem: function(rec)
19823     {
19824         var valueField = this.combo.valueField;
19825         var displayField = this.combo.displayField;
19826         if (this.items.indexOfKey(rec[valueField]) > -1) {
19827             //console.log("GOT " + rec.data.id);
19828             return;
19829         }
19830         
19831         var x = new Roo.form.ComboBoxArray.Item({
19832             //id : rec[this.idField],
19833             data : rec,
19834             displayField : displayField ,
19835             tipField : displayField ,
19836             cb : this
19837         });
19838         // use the 
19839         this.items.add(rec[valueField],x);
19840         // add it before the element..
19841         this.updateHiddenEl();
19842         x.render(this.outerWrap, this.wrap.dom);
19843         // add the image handler..
19844     },
19845     
19846     updateHiddenEl : function()
19847     {
19848         this.validate();
19849         if (!this.hiddenEl) {
19850             return;
19851         }
19852         var ar = [];
19853         var idField = this.combo.valueField;
19854         
19855         this.items.each(function(f) {
19856             ar.push(f.data[idField]);
19857            
19858         });
19859         this.hiddenEl.dom.value = ar.join(',');
19860         this.validate();
19861     },
19862     
19863     reset : function()
19864     {
19865         this.items.clear();
19866         
19867         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19868            el.remove();
19869         });
19870         
19871         this.el.dom.value = '';
19872         if (this.hiddenEl) {
19873             this.hiddenEl.dom.value = '';
19874         }
19875         
19876     },
19877     getValue: function()
19878     {
19879         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19880     },
19881     setValue: function(v) // not a valid action - must use addItems..
19882     {
19883          
19884         this.reset();
19885         
19886         
19887         
19888         if (this.store.isLocal && (typeof(v) == 'string')) {
19889             // then we can use the store to find the values..
19890             // comma seperated at present.. this needs to allow JSON based encoding..
19891             this.hiddenEl.value  = v;
19892             var v_ar = [];
19893             Roo.each(v.split(','), function(k) {
19894                 Roo.log("CHECK " + this.valueField + ',' + k);
19895                 var li = this.store.query(this.valueField, k);
19896                 if (!li.length) {
19897                     return;
19898                 }
19899                 var add = {};
19900                 add[this.valueField] = k;
19901                 add[this.displayField] = li.item(0).data[this.displayField];
19902                 
19903                 this.addItem(add);
19904             }, this) 
19905              
19906         }
19907         if (typeof(v) == 'object' ) {
19908             // then let's assume it's an array of objects..
19909             Roo.each(v, function(l) {
19910                 this.addItem(l);
19911             }, this);
19912              
19913         }
19914         
19915         
19916     },
19917     setFromData: function(v)
19918     {
19919         // this recieves an object, if setValues is called.
19920         this.reset();
19921         this.el.dom.value = v[this.displayField];
19922         this.hiddenEl.dom.value = v[this.valueField];
19923         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19924             return;
19925         }
19926         var kv = v[this.valueField];
19927         var dv = v[this.displayField];
19928         kv = typeof(kv) != 'string' ? '' : kv;
19929         dv = typeof(dv) != 'string' ? '' : dv;
19930         
19931         
19932         var keys = kv.split(',');
19933         var display = dv.split(',');
19934         for (var i = 0 ; i < keys.length; i++) {
19935             
19936             add = {};
19937             add[this.valueField] = keys[i];
19938             add[this.displayField] = display[i];
19939             this.addItem(add);
19940         }
19941       
19942         
19943     },
19944     
19945     /**
19946      * Validates the combox array value
19947      * @return {Boolean} True if the value is valid, else false
19948      */
19949     validate : function(){
19950         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19951             this.clearInvalid();
19952             return true;
19953         }
19954         return false;
19955     },
19956     
19957     validateValue : function(value){
19958         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19959         
19960     },
19961     
19962     /*@
19963      * overide
19964      * 
19965      */
19966     isDirty : function() {
19967         if(this.disabled) {
19968             return false;
19969         }
19970         
19971         try {
19972             var d = Roo.decode(String(this.originalValue));
19973         } catch (e) {
19974             return String(this.getValue()) !== String(this.originalValue);
19975         }
19976         
19977         var originalValue = [];
19978         
19979         for (var i = 0; i < d.length; i++){
19980             originalValue.push(d[i][this.valueField]);
19981         }
19982         
19983         return String(this.getValue()) !== String(originalValue.join(','));
19984         
19985     }
19986     
19987 });
19988
19989
19990
19991 /**
19992  * @class Roo.form.ComboBoxArray.Item
19993  * @extends Roo.BoxComponent
19994  * A selected item in the list
19995  *  Fred [x]  Brian [x]  [Pick another |v]
19996  * 
19997  * @constructor
19998  * Create a new item.
19999  * @param {Object} config Configuration options
20000  */
20001  
20002 Roo.form.ComboBoxArray.Item = function(config) {
20003     config.id = Roo.id();
20004     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
20005 }
20006
20007 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
20008     data : {},
20009     cb: false,
20010     displayField : false,
20011     tipField : false,
20012     
20013     
20014     defaultAutoCreate : {
20015         tag: 'div',
20016         cls: 'x-cbarray-item',
20017         cn : [ 
20018             { tag: 'div' },
20019             {
20020                 tag: 'img',
20021                 width:16,
20022                 height : 16,
20023                 src : Roo.BLANK_IMAGE_URL ,
20024                 align: 'center'
20025             }
20026         ]
20027         
20028     },
20029     
20030  
20031     onRender : function(ct, position)
20032     {
20033         Roo.form.Field.superclass.onRender.call(this, ct, position);
20034         
20035         if(!this.el){
20036             var cfg = this.getAutoCreate();
20037             this.el = ct.createChild(cfg, position);
20038         }
20039         
20040         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
20041         
20042         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
20043             this.cb.renderer(this.data) :
20044             String.format('{0}',this.data[this.displayField]);
20045         
20046             
20047         this.el.child('div').dom.setAttribute('qtip',
20048                         String.format('{0}',this.data[this.tipField])
20049         );
20050         
20051         this.el.child('img').on('click', this.remove, this);
20052         
20053     },
20054    
20055     remove : function()
20056     {
20057         if(this.cb.disabled){
20058             return;
20059         }
20060         
20061         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
20062             this.cb.items.remove(this);
20063             this.el.child('img').un('click', this.remove, this);
20064             this.el.remove();
20065             this.cb.updateHiddenEl();
20066
20067             this.cb.fireEvent('remove', this.cb, this);
20068         }
20069         
20070     }
20071 });/*
20072  * Based on:
20073  * Ext JS Library 1.1.1
20074  * Copyright(c) 2006-2007, Ext JS, LLC.
20075  *
20076  * Originally Released Under LGPL - original licence link has changed is not relivant.
20077  *
20078  * Fork - LGPL
20079  * <script type="text/javascript">
20080  */
20081 /**
20082  * @class Roo.form.Checkbox
20083  * @extends Roo.form.Field
20084  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20085  * @constructor
20086  * Creates a new Checkbox
20087  * @param {Object} config Configuration options
20088  */
20089 Roo.form.Checkbox = function(config){
20090     Roo.form.Checkbox.superclass.constructor.call(this, config);
20091     this.addEvents({
20092         /**
20093          * @event check
20094          * Fires when the checkbox is checked or unchecked.
20095              * @param {Roo.form.Checkbox} this This checkbox
20096              * @param {Boolean} checked The new checked value
20097              */
20098         check : true
20099     });
20100 };
20101
20102 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20103     /**
20104      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20105      */
20106     focusClass : undefined,
20107     /**
20108      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20109      */
20110     fieldClass: "x-form-field",
20111     /**
20112      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20113      */
20114     checked: false,
20115     /**
20116      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20117      * {tag: "input", type: "checkbox", autocomplete: "off"})
20118      */
20119     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20120     /**
20121      * @cfg {String} boxLabel The text that appears beside the checkbox
20122      */
20123     boxLabel : "",
20124     /**
20125      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20126      */  
20127     inputValue : '1',
20128     /**
20129      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20130      */
20131      valueOff: '0', // value when not checked..
20132
20133     actionMode : 'viewEl', 
20134     //
20135     // private
20136     itemCls : 'x-menu-check-item x-form-item',
20137     groupClass : 'x-menu-group-item',
20138     inputType : 'hidden',
20139     
20140     
20141     inSetChecked: false, // check that we are not calling self...
20142     
20143     inputElement: false, // real input element?
20144     basedOn: false, // ????
20145     
20146     isFormField: true, // not sure where this is needed!!!!
20147
20148     onResize : function(){
20149         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20150         if(!this.boxLabel){
20151             this.el.alignTo(this.wrap, 'c-c');
20152         }
20153     },
20154
20155     initEvents : function(){
20156         Roo.form.Checkbox.superclass.initEvents.call(this);
20157         this.el.on("click", this.onClick,  this);
20158         this.el.on("change", this.onClick,  this);
20159     },
20160
20161
20162     getResizeEl : function(){
20163         return this.wrap;
20164     },
20165
20166     getPositionEl : function(){
20167         return this.wrap;
20168     },
20169
20170     // private
20171     onRender : function(ct, position){
20172         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20173         /*
20174         if(this.inputValue !== undefined){
20175             this.el.dom.value = this.inputValue;
20176         }
20177         */
20178         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20179         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20180         var viewEl = this.wrap.createChild({ 
20181             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20182         this.viewEl = viewEl;   
20183         this.wrap.on('click', this.onClick,  this); 
20184         
20185         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20186         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20187         
20188         
20189         
20190         if(this.boxLabel){
20191             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20192         //    viewEl.on('click', this.onClick,  this); 
20193         }
20194         //if(this.checked){
20195             this.setChecked(this.checked);
20196         //}else{
20197             //this.checked = this.el.dom;
20198         //}
20199
20200     },
20201
20202     // private
20203     initValue : Roo.emptyFn,
20204
20205     /**
20206      * Returns the checked state of the checkbox.
20207      * @return {Boolean} True if checked, else false
20208      */
20209     getValue : function(){
20210         if(this.el){
20211             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20212         }
20213         return this.valueOff;
20214         
20215     },
20216
20217         // private
20218     onClick : function(){ 
20219         if (this.disabled) {
20220             return;
20221         }
20222         this.setChecked(!this.checked);
20223
20224         //if(this.el.dom.checked != this.checked){
20225         //    this.setValue(this.el.dom.checked);
20226        // }
20227     },
20228
20229     /**
20230      * Sets the checked state of the checkbox.
20231      * On is always based on a string comparison between inputValue and the param.
20232      * @param {Boolean/String} value - the value to set 
20233      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20234      */
20235     setValue : function(v,suppressEvent){
20236         
20237         
20238         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20239         //if(this.el && this.el.dom){
20240         //    this.el.dom.checked = this.checked;
20241         //    this.el.dom.defaultChecked = this.checked;
20242         //}
20243         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20244         //this.fireEvent("check", this, this.checked);
20245     },
20246     // private..
20247     setChecked : function(state,suppressEvent)
20248     {
20249         if (this.inSetChecked) {
20250             this.checked = state;
20251             return;
20252         }
20253         
20254     
20255         if(this.wrap){
20256             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20257         }
20258         this.checked = state;
20259         if(suppressEvent !== true){
20260             this.fireEvent('check', this, state);
20261         }
20262         this.inSetChecked = true;
20263         this.el.dom.value = state ? this.inputValue : this.valueOff;
20264         this.inSetChecked = false;
20265         
20266     },
20267     // handle setting of hidden value by some other method!!?!?
20268     setFromHidden: function()
20269     {
20270         if(!this.el){
20271             return;
20272         }
20273         //console.log("SET FROM HIDDEN");
20274         //alert('setFrom hidden');
20275         this.setValue(this.el.dom.value);
20276     },
20277     
20278     onDestroy : function()
20279     {
20280         if(this.viewEl){
20281             Roo.get(this.viewEl).remove();
20282         }
20283          
20284         Roo.form.Checkbox.superclass.onDestroy.call(this);
20285     },
20286     
20287     setBoxLabel : function(str)
20288     {
20289         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20290     }
20291
20292 });/*
20293  * Based on:
20294  * Ext JS Library 1.1.1
20295  * Copyright(c) 2006-2007, Ext JS, LLC.
20296  *
20297  * Originally Released Under LGPL - original licence link has changed is not relivant.
20298  *
20299  * Fork - LGPL
20300  * <script type="text/javascript">
20301  */
20302  
20303 /**
20304  * @class Roo.form.Radio
20305  * @extends Roo.form.Checkbox
20306  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20307  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20308  * @constructor
20309  * Creates a new Radio
20310  * @param {Object} config Configuration options
20311  */
20312 Roo.form.Radio = function(){
20313     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20314 };
20315 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20316     inputType: 'radio',
20317
20318     /**
20319      * If this radio is part of a group, it will return the selected value
20320      * @return {String}
20321      */
20322     getGroupValue : function(){
20323         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20324     },
20325     
20326     
20327     onRender : function(ct, position){
20328         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20329         
20330         if(this.inputValue !== undefined){
20331             this.el.dom.value = this.inputValue;
20332         }
20333          
20334         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20335         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20336         //var viewEl = this.wrap.createChild({ 
20337         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20338         //this.viewEl = viewEl;   
20339         //this.wrap.on('click', this.onClick,  this); 
20340         
20341         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20342         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20343         
20344         
20345         
20346         if(this.boxLabel){
20347             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20348         //    viewEl.on('click', this.onClick,  this); 
20349         }
20350          if(this.checked){
20351             this.el.dom.checked =   'checked' ;
20352         }
20353          
20354     } 
20355     
20356     
20357 });//<script type="text/javascript">
20358
20359 /*
20360  * Based  Ext JS Library 1.1.1
20361  * Copyright(c) 2006-2007, Ext JS, LLC.
20362  * LGPL
20363  *
20364  */
20365  
20366 /**
20367  * @class Roo.HtmlEditorCore
20368  * @extends Roo.Component
20369  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20370  *
20371  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20372  */
20373
20374 Roo.HtmlEditorCore = function(config){
20375     
20376     
20377     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20378     
20379     
20380     this.addEvents({
20381         /**
20382          * @event initialize
20383          * Fires when the editor is fully initialized (including the iframe)
20384          * @param {Roo.HtmlEditorCore} this
20385          */
20386         initialize: true,
20387         /**
20388          * @event activate
20389          * Fires when the editor is first receives the focus. Any insertion must wait
20390          * until after this event.
20391          * @param {Roo.HtmlEditorCore} this
20392          */
20393         activate: true,
20394          /**
20395          * @event beforesync
20396          * Fires before the textarea is updated with content from the editor iframe. Return false
20397          * to cancel the sync.
20398          * @param {Roo.HtmlEditorCore} this
20399          * @param {String} html
20400          */
20401         beforesync: true,
20402          /**
20403          * @event beforepush
20404          * Fires before the iframe editor is updated with content from the textarea. Return false
20405          * to cancel the push.
20406          * @param {Roo.HtmlEditorCore} this
20407          * @param {String} html
20408          */
20409         beforepush: true,
20410          /**
20411          * @event sync
20412          * Fires when the textarea is updated with content from the editor iframe.
20413          * @param {Roo.HtmlEditorCore} this
20414          * @param {String} html
20415          */
20416         sync: true,
20417          /**
20418          * @event push
20419          * Fires when the iframe editor is updated with content from the textarea.
20420          * @param {Roo.HtmlEditorCore} this
20421          * @param {String} html
20422          */
20423         push: true,
20424         
20425         /**
20426          * @event editorevent
20427          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20428          * @param {Roo.HtmlEditorCore} this
20429          */
20430         editorevent: true
20431         
20432     });
20433     
20434     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20435     
20436     // defaults : white / black...
20437     this.applyBlacklists();
20438     
20439     
20440     
20441 };
20442
20443
20444 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20445
20446
20447      /**
20448      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20449      */
20450     
20451     owner : false,
20452     
20453      /**
20454      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20455      *                        Roo.resizable.
20456      */
20457     resizable : false,
20458      /**
20459      * @cfg {Number} height (in pixels)
20460      */   
20461     height: 300,
20462    /**
20463      * @cfg {Number} width (in pixels)
20464      */   
20465     width: 500,
20466     
20467     /**
20468      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20469      * 
20470      */
20471     stylesheets: false,
20472     
20473     // id of frame..
20474     frameId: false,
20475     
20476     // private properties
20477     validationEvent : false,
20478     deferHeight: true,
20479     initialized : false,
20480     activated : false,
20481     sourceEditMode : false,
20482     onFocus : Roo.emptyFn,
20483     iframePad:3,
20484     hideMode:'offsets',
20485     
20486     clearUp: true,
20487     
20488     // blacklist + whitelisted elements..
20489     black: false,
20490     white: false,
20491      
20492     bodyCls : '',
20493
20494     /**
20495      * Protected method that will not generally be called directly. It
20496      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20497      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20498      */
20499     getDocMarkup : function(){
20500         // body styles..
20501         var st = '';
20502         
20503         // inherit styels from page...?? 
20504         if (this.stylesheets === false) {
20505             
20506             Roo.get(document.head).select('style').each(function(node) {
20507                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20508             });
20509             
20510             Roo.get(document.head).select('link').each(function(node) { 
20511                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20512             });
20513             
20514         } else if (!this.stylesheets.length) {
20515                 // simple..
20516                 st = '<style type="text/css">' +
20517                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20518                    '</style>';
20519         } else { 
20520             st = '<style type="text/css">' +
20521                     this.stylesheets +
20522                 '</style>';
20523         }
20524         
20525         st +=  '<style type="text/css">' +
20526             'IMG { cursor: pointer } ' +
20527         '</style>';
20528
20529         var cls = 'roo-htmleditor-body';
20530         
20531         if(this.bodyCls.length){
20532             cls += ' ' + this.bodyCls;
20533         }
20534         
20535         return '<html><head>' + st  +
20536             //<style type="text/css">' +
20537             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20538             //'</style>' +
20539             ' </head><body class="' +  cls + '"></body></html>';
20540     },
20541
20542     // private
20543     onRender : function(ct, position)
20544     {
20545         var _t = this;
20546         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20547         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20548         
20549         
20550         this.el.dom.style.border = '0 none';
20551         this.el.dom.setAttribute('tabIndex', -1);
20552         this.el.addClass('x-hidden hide');
20553         
20554         
20555         
20556         if(Roo.isIE){ // fix IE 1px bogus margin
20557             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20558         }
20559        
20560         
20561         this.frameId = Roo.id();
20562         
20563          
20564         
20565         var iframe = this.owner.wrap.createChild({
20566             tag: 'iframe',
20567             cls: 'form-control', // bootstrap..
20568             id: this.frameId,
20569             name: this.frameId,
20570             frameBorder : 'no',
20571             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20572         }, this.el
20573         );
20574         
20575         
20576         this.iframe = iframe.dom;
20577
20578          this.assignDocWin();
20579         
20580         this.doc.designMode = 'on';
20581        
20582         this.doc.open();
20583         this.doc.write(this.getDocMarkup());
20584         this.doc.close();
20585
20586         
20587         var task = { // must defer to wait for browser to be ready
20588             run : function(){
20589                 //console.log("run task?" + this.doc.readyState);
20590                 this.assignDocWin();
20591                 if(this.doc.body || this.doc.readyState == 'complete'){
20592                     try {
20593                         this.doc.designMode="on";
20594                     } catch (e) {
20595                         return;
20596                     }
20597                     Roo.TaskMgr.stop(task);
20598                     this.initEditor.defer(10, this);
20599                 }
20600             },
20601             interval : 10,
20602             duration: 10000,
20603             scope: this
20604         };
20605         Roo.TaskMgr.start(task);
20606
20607     },
20608
20609     // private
20610     onResize : function(w, h)
20611     {
20612          Roo.log('resize: ' +w + ',' + h );
20613         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20614         if(!this.iframe){
20615             return;
20616         }
20617         if(typeof w == 'number'){
20618             
20619             this.iframe.style.width = w + 'px';
20620         }
20621         if(typeof h == 'number'){
20622             
20623             this.iframe.style.height = h + 'px';
20624             if(this.doc){
20625                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20626             }
20627         }
20628         
20629     },
20630
20631     /**
20632      * Toggles the editor between standard and source edit mode.
20633      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20634      */
20635     toggleSourceEdit : function(sourceEditMode){
20636         
20637         this.sourceEditMode = sourceEditMode === true;
20638         
20639         if(this.sourceEditMode){
20640  
20641             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20642             
20643         }else{
20644             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20645             //this.iframe.className = '';
20646             this.deferFocus();
20647         }
20648         //this.setSize(this.owner.wrap.getSize());
20649         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20650     },
20651
20652     
20653   
20654
20655     /**
20656      * Protected method that will not generally be called directly. If you need/want
20657      * custom HTML cleanup, this is the method you should override.
20658      * @param {String} html The HTML to be cleaned
20659      * return {String} The cleaned HTML
20660      */
20661     cleanHtml : function(html){
20662         html = String(html);
20663         if(html.length > 5){
20664             if(Roo.isSafari){ // strip safari nonsense
20665                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20666             }
20667         }
20668         if(html == '&nbsp;'){
20669             html = '';
20670         }
20671         return html;
20672     },
20673
20674     /**
20675      * HTML Editor -> Textarea
20676      * Protected method that will not generally be called directly. Syncs the contents
20677      * of the editor iframe with the textarea.
20678      */
20679     syncValue : function(){
20680         if(this.initialized){
20681             var bd = (this.doc.body || this.doc.documentElement);
20682             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20683             var html = bd.innerHTML;
20684             if(Roo.isSafari){
20685                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20686                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20687                 if(m && m[1]){
20688                     html = '<div style="'+m[0]+'">' + html + '</div>';
20689                 }
20690             }
20691             html = this.cleanHtml(html);
20692             // fix up the special chars.. normaly like back quotes in word...
20693             // however we do not want to do this with chinese..
20694             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20695                 var cc = b.charCodeAt();
20696                 if (
20697                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20698                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20699                     (cc >= 0xf900 && cc < 0xfb00 )
20700                 ) {
20701                         return b;
20702                 }
20703                 return "&#"+cc+";" 
20704             });
20705             if(this.owner.fireEvent('beforesync', this, html) !== false){
20706                 this.el.dom.value = html;
20707                 this.owner.fireEvent('sync', this, html);
20708             }
20709         }
20710     },
20711
20712     /**
20713      * Protected method that will not generally be called directly. Pushes the value of the textarea
20714      * into the iframe editor.
20715      */
20716     pushValue : function(){
20717         if(this.initialized){
20718             var v = this.el.dom.value.trim();
20719             
20720 //            if(v.length < 1){
20721 //                v = '&#160;';
20722 //            }
20723             
20724             if(this.owner.fireEvent('beforepush', this, v) !== false){
20725                 var d = (this.doc.body || this.doc.documentElement);
20726                 d.innerHTML = v;
20727                 this.cleanUpPaste();
20728                 this.el.dom.value = d.innerHTML;
20729                 this.owner.fireEvent('push', this, v);
20730             }
20731         }
20732     },
20733
20734     // private
20735     deferFocus : function(){
20736         this.focus.defer(10, this);
20737     },
20738
20739     // doc'ed in Field
20740     focus : function(){
20741         if(this.win && !this.sourceEditMode){
20742             this.win.focus();
20743         }else{
20744             this.el.focus();
20745         }
20746     },
20747     
20748     assignDocWin: function()
20749     {
20750         var iframe = this.iframe;
20751         
20752          if(Roo.isIE){
20753             this.doc = iframe.contentWindow.document;
20754             this.win = iframe.contentWindow;
20755         } else {
20756 //            if (!Roo.get(this.frameId)) {
20757 //                return;
20758 //            }
20759 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20760 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20761             
20762             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20763                 return;
20764             }
20765             
20766             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20767             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20768         }
20769     },
20770     
20771     // private
20772     initEditor : function(){
20773         //console.log("INIT EDITOR");
20774         this.assignDocWin();
20775         
20776         
20777         
20778         this.doc.designMode="on";
20779         this.doc.open();
20780         this.doc.write(this.getDocMarkup());
20781         this.doc.close();
20782         
20783         var dbody = (this.doc.body || this.doc.documentElement);
20784         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20785         // this copies styles from the containing element into thsi one..
20786         // not sure why we need all of this..
20787         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20788         
20789         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20790         //ss['background-attachment'] = 'fixed'; // w3c
20791         dbody.bgProperties = 'fixed'; // ie
20792         //Roo.DomHelper.applyStyles(dbody, ss);
20793         Roo.EventManager.on(this.doc, {
20794             //'mousedown': this.onEditorEvent,
20795             'mouseup': this.onEditorEvent,
20796             'dblclick': this.onEditorEvent,
20797             'click': this.onEditorEvent,
20798             'keyup': this.onEditorEvent,
20799             buffer:100,
20800             scope: this
20801         });
20802         if(Roo.isGecko){
20803             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20804         }
20805         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20806             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20807         }
20808         this.initialized = true;
20809
20810         this.owner.fireEvent('initialize', this);
20811         this.pushValue();
20812     },
20813
20814     // private
20815     onDestroy : function(){
20816         
20817         
20818         
20819         if(this.rendered){
20820             
20821             //for (var i =0; i < this.toolbars.length;i++) {
20822             //    // fixme - ask toolbars for heights?
20823             //    this.toolbars[i].onDestroy();
20824            // }
20825             
20826             //this.wrap.dom.innerHTML = '';
20827             //this.wrap.remove();
20828         }
20829     },
20830
20831     // private
20832     onFirstFocus : function(){
20833         
20834         this.assignDocWin();
20835         
20836         
20837         this.activated = true;
20838          
20839     
20840         if(Roo.isGecko){ // prevent silly gecko errors
20841             this.win.focus();
20842             var s = this.win.getSelection();
20843             if(!s.focusNode || s.focusNode.nodeType != 3){
20844                 var r = s.getRangeAt(0);
20845                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20846                 r.collapse(true);
20847                 this.deferFocus();
20848             }
20849             try{
20850                 this.execCmd('useCSS', true);
20851                 this.execCmd('styleWithCSS', false);
20852             }catch(e){}
20853         }
20854         this.owner.fireEvent('activate', this);
20855     },
20856
20857     // private
20858     adjustFont: function(btn){
20859         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20860         //if(Roo.isSafari){ // safari
20861         //    adjust *= 2;
20862        // }
20863         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20864         if(Roo.isSafari){ // safari
20865             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20866             v =  (v < 10) ? 10 : v;
20867             v =  (v > 48) ? 48 : v;
20868             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20869             
20870         }
20871         
20872         
20873         v = Math.max(1, v+adjust);
20874         
20875         this.execCmd('FontSize', v  );
20876     },
20877
20878     onEditorEvent : function(e)
20879     {
20880         this.owner.fireEvent('editorevent', this, e);
20881       //  this.updateToolbar();
20882         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20883     },
20884
20885     insertTag : function(tg)
20886     {
20887         // could be a bit smarter... -> wrap the current selected tRoo..
20888         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20889             
20890             range = this.createRange(this.getSelection());
20891             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20892             wrappingNode.appendChild(range.extractContents());
20893             range.insertNode(wrappingNode);
20894
20895             return;
20896             
20897             
20898             
20899         }
20900         this.execCmd("formatblock",   tg);
20901         
20902     },
20903     
20904     insertText : function(txt)
20905     {
20906         
20907         
20908         var range = this.createRange();
20909         range.deleteContents();
20910                //alert(Sender.getAttribute('label'));
20911                
20912         range.insertNode(this.doc.createTextNode(txt));
20913     } ,
20914     
20915      
20916
20917     /**
20918      * Executes a Midas editor command on the editor document and performs necessary focus and
20919      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20920      * @param {String} cmd The Midas command
20921      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20922      */
20923     relayCmd : function(cmd, value){
20924         this.win.focus();
20925         this.execCmd(cmd, value);
20926         this.owner.fireEvent('editorevent', this);
20927         //this.updateToolbar();
20928         this.owner.deferFocus();
20929     },
20930
20931     /**
20932      * Executes a Midas editor command directly on the editor document.
20933      * For visual commands, you should use {@link #relayCmd} instead.
20934      * <b>This should only be called after the editor is initialized.</b>
20935      * @param {String} cmd The Midas command
20936      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20937      */
20938     execCmd : function(cmd, value){
20939         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20940         this.syncValue();
20941     },
20942  
20943  
20944    
20945     /**
20946      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20947      * to insert tRoo.
20948      * @param {String} text | dom node.. 
20949      */
20950     insertAtCursor : function(text)
20951     {
20952         
20953         if(!this.activated){
20954             return;
20955         }
20956         /*
20957         if(Roo.isIE){
20958             this.win.focus();
20959             var r = this.doc.selection.createRange();
20960             if(r){
20961                 r.collapse(true);
20962                 r.pasteHTML(text);
20963                 this.syncValue();
20964                 this.deferFocus();
20965             
20966             }
20967             return;
20968         }
20969         */
20970         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20971             this.win.focus();
20972             
20973             
20974             // from jquery ui (MIT licenced)
20975             var range, node;
20976             var win = this.win;
20977             
20978             if (win.getSelection && win.getSelection().getRangeAt) {
20979                 range = win.getSelection().getRangeAt(0);
20980                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20981                 range.insertNode(node);
20982             } else if (win.document.selection && win.document.selection.createRange) {
20983                 // no firefox support
20984                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20985                 win.document.selection.createRange().pasteHTML(txt);
20986             } else {
20987                 // no firefox support
20988                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20989                 this.execCmd('InsertHTML', txt);
20990             } 
20991             
20992             this.syncValue();
20993             
20994             this.deferFocus();
20995         }
20996     },
20997  // private
20998     mozKeyPress : function(e){
20999         if(e.ctrlKey){
21000             var c = e.getCharCode(), cmd;
21001           
21002             if(c > 0){
21003                 c = String.fromCharCode(c).toLowerCase();
21004                 switch(c){
21005                     case 'b':
21006                         cmd = 'bold';
21007                         break;
21008                     case 'i':
21009                         cmd = 'italic';
21010                         break;
21011                     
21012                     case 'u':
21013                         cmd = 'underline';
21014                         break;
21015                     
21016                     case 'v':
21017                         this.cleanUpPaste.defer(100, this);
21018                         return;
21019                         
21020                 }
21021                 if(cmd){
21022                     this.win.focus();
21023                     this.execCmd(cmd);
21024                     this.deferFocus();
21025                     e.preventDefault();
21026                 }
21027                 
21028             }
21029         }
21030     },
21031
21032     // private
21033     fixKeys : function(){ // load time branching for fastest keydown performance
21034         if(Roo.isIE){
21035             return function(e){
21036                 var k = e.getKey(), r;
21037                 if(k == e.TAB){
21038                     e.stopEvent();
21039                     r = this.doc.selection.createRange();
21040                     if(r){
21041                         r.collapse(true);
21042                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21043                         this.deferFocus();
21044                     }
21045                     return;
21046                 }
21047                 
21048                 if(k == e.ENTER){
21049                     r = this.doc.selection.createRange();
21050                     if(r){
21051                         var target = r.parentElement();
21052                         if(!target || target.tagName.toLowerCase() != 'li'){
21053                             e.stopEvent();
21054                             r.pasteHTML('<br />');
21055                             r.collapse(false);
21056                             r.select();
21057                         }
21058                     }
21059                 }
21060                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21061                     this.cleanUpPaste.defer(100, this);
21062                     return;
21063                 }
21064                 
21065                 
21066             };
21067         }else if(Roo.isOpera){
21068             return function(e){
21069                 var k = e.getKey();
21070                 if(k == e.TAB){
21071                     e.stopEvent();
21072                     this.win.focus();
21073                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21074                     this.deferFocus();
21075                 }
21076                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21077                     this.cleanUpPaste.defer(100, this);
21078                     return;
21079                 }
21080                 
21081             };
21082         }else if(Roo.isSafari){
21083             return function(e){
21084                 var k = e.getKey();
21085                 
21086                 if(k == e.TAB){
21087                     e.stopEvent();
21088                     this.execCmd('InsertText','\t');
21089                     this.deferFocus();
21090                     return;
21091                 }
21092                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21093                     this.cleanUpPaste.defer(100, this);
21094                     return;
21095                 }
21096                 
21097              };
21098         }
21099     }(),
21100     
21101     getAllAncestors: function()
21102     {
21103         var p = this.getSelectedNode();
21104         var a = [];
21105         if (!p) {
21106             a.push(p); // push blank onto stack..
21107             p = this.getParentElement();
21108         }
21109         
21110         
21111         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21112             a.push(p);
21113             p = p.parentNode;
21114         }
21115         a.push(this.doc.body);
21116         return a;
21117     },
21118     lastSel : false,
21119     lastSelNode : false,
21120     
21121     
21122     getSelection : function() 
21123     {
21124         this.assignDocWin();
21125         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21126     },
21127     
21128     getSelectedNode: function() 
21129     {
21130         // this may only work on Gecko!!!
21131         
21132         // should we cache this!!!!
21133         
21134         
21135         
21136          
21137         var range = this.createRange(this.getSelection()).cloneRange();
21138         
21139         if (Roo.isIE) {
21140             var parent = range.parentElement();
21141             while (true) {
21142                 var testRange = range.duplicate();
21143                 testRange.moveToElementText(parent);
21144                 if (testRange.inRange(range)) {
21145                     break;
21146                 }
21147                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21148                     break;
21149                 }
21150                 parent = parent.parentElement;
21151             }
21152             return parent;
21153         }
21154         
21155         // is ancestor a text element.
21156         var ac =  range.commonAncestorContainer;
21157         if (ac.nodeType == 3) {
21158             ac = ac.parentNode;
21159         }
21160         
21161         var ar = ac.childNodes;
21162          
21163         var nodes = [];
21164         var other_nodes = [];
21165         var has_other_nodes = false;
21166         for (var i=0;i<ar.length;i++) {
21167             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21168                 continue;
21169             }
21170             // fullly contained node.
21171             
21172             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21173                 nodes.push(ar[i]);
21174                 continue;
21175             }
21176             
21177             // probably selected..
21178             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21179                 other_nodes.push(ar[i]);
21180                 continue;
21181             }
21182             // outer..
21183             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21184                 continue;
21185             }
21186             
21187             
21188             has_other_nodes = true;
21189         }
21190         if (!nodes.length && other_nodes.length) {
21191             nodes= other_nodes;
21192         }
21193         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21194             return false;
21195         }
21196         
21197         return nodes[0];
21198     },
21199     createRange: function(sel)
21200     {
21201         // this has strange effects when using with 
21202         // top toolbar - not sure if it's a great idea.
21203         //this.editor.contentWindow.focus();
21204         if (typeof sel != "undefined") {
21205             try {
21206                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21207             } catch(e) {
21208                 return this.doc.createRange();
21209             }
21210         } else {
21211             return this.doc.createRange();
21212         }
21213     },
21214     getParentElement: function()
21215     {
21216         
21217         this.assignDocWin();
21218         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21219         
21220         var range = this.createRange(sel);
21221          
21222         try {
21223             var p = range.commonAncestorContainer;
21224             while (p.nodeType == 3) { // text node
21225                 p = p.parentNode;
21226             }
21227             return p;
21228         } catch (e) {
21229             return null;
21230         }
21231     
21232     },
21233     /***
21234      *
21235      * Range intersection.. the hard stuff...
21236      *  '-1' = before
21237      *  '0' = hits..
21238      *  '1' = after.
21239      *         [ -- selected range --- ]
21240      *   [fail]                        [fail]
21241      *
21242      *    basically..
21243      *      if end is before start or  hits it. fail.
21244      *      if start is after end or hits it fail.
21245      *
21246      *   if either hits (but other is outside. - then it's not 
21247      *   
21248      *    
21249      **/
21250     
21251     
21252     // @see http://www.thismuchiknow.co.uk/?p=64.
21253     rangeIntersectsNode : function(range, node)
21254     {
21255         var nodeRange = node.ownerDocument.createRange();
21256         try {
21257             nodeRange.selectNode(node);
21258         } catch (e) {
21259             nodeRange.selectNodeContents(node);
21260         }
21261     
21262         var rangeStartRange = range.cloneRange();
21263         rangeStartRange.collapse(true);
21264     
21265         var rangeEndRange = range.cloneRange();
21266         rangeEndRange.collapse(false);
21267     
21268         var nodeStartRange = nodeRange.cloneRange();
21269         nodeStartRange.collapse(true);
21270     
21271         var nodeEndRange = nodeRange.cloneRange();
21272         nodeEndRange.collapse(false);
21273     
21274         return rangeStartRange.compareBoundaryPoints(
21275                  Range.START_TO_START, nodeEndRange) == -1 &&
21276                rangeEndRange.compareBoundaryPoints(
21277                  Range.START_TO_START, nodeStartRange) == 1;
21278         
21279          
21280     },
21281     rangeCompareNode : function(range, node)
21282     {
21283         var nodeRange = node.ownerDocument.createRange();
21284         try {
21285             nodeRange.selectNode(node);
21286         } catch (e) {
21287             nodeRange.selectNodeContents(node);
21288         }
21289         
21290         
21291         range.collapse(true);
21292     
21293         nodeRange.collapse(true);
21294      
21295         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21296         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21297          
21298         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21299         
21300         var nodeIsBefore   =  ss == 1;
21301         var nodeIsAfter    = ee == -1;
21302         
21303         if (nodeIsBefore && nodeIsAfter) {
21304             return 0; // outer
21305         }
21306         if (!nodeIsBefore && nodeIsAfter) {
21307             return 1; //right trailed.
21308         }
21309         
21310         if (nodeIsBefore && !nodeIsAfter) {
21311             return 2;  // left trailed.
21312         }
21313         // fully contined.
21314         return 3;
21315     },
21316
21317     // private? - in a new class?
21318     cleanUpPaste :  function()
21319     {
21320         // cleans up the whole document..
21321         Roo.log('cleanuppaste');
21322         
21323         this.cleanUpChildren(this.doc.body);
21324         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21325         if (clean != this.doc.body.innerHTML) {
21326             this.doc.body.innerHTML = clean;
21327         }
21328         
21329     },
21330     
21331     cleanWordChars : function(input) {// change the chars to hex code
21332         var he = Roo.HtmlEditorCore;
21333         
21334         var output = input;
21335         Roo.each(he.swapCodes, function(sw) { 
21336             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21337             
21338             output = output.replace(swapper, sw[1]);
21339         });
21340         
21341         return output;
21342     },
21343     
21344     
21345     cleanUpChildren : function (n)
21346     {
21347         if (!n.childNodes.length) {
21348             return;
21349         }
21350         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21351            this.cleanUpChild(n.childNodes[i]);
21352         }
21353     },
21354     
21355     
21356         
21357     
21358     cleanUpChild : function (node)
21359     {
21360         var ed = this;
21361         //console.log(node);
21362         if (node.nodeName == "#text") {
21363             // clean up silly Windows -- stuff?
21364             return; 
21365         }
21366         if (node.nodeName == "#comment") {
21367             node.parentNode.removeChild(node);
21368             // clean up silly Windows -- stuff?
21369             return; 
21370         }
21371         var lcname = node.tagName.toLowerCase();
21372         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21373         // whitelist of tags..
21374         
21375         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21376             // remove node.
21377             node.parentNode.removeChild(node);
21378             return;
21379             
21380         }
21381         
21382         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21383         
21384         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21385         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21386         
21387         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21388         //    remove_keep_children = true;
21389         //}
21390         
21391         if (remove_keep_children) {
21392             this.cleanUpChildren(node);
21393             // inserts everything just before this node...
21394             while (node.childNodes.length) {
21395                 var cn = node.childNodes[0];
21396                 node.removeChild(cn);
21397                 node.parentNode.insertBefore(cn, node);
21398             }
21399             node.parentNode.removeChild(node);
21400             return;
21401         }
21402         
21403         if (!node.attributes || !node.attributes.length) {
21404             this.cleanUpChildren(node);
21405             return;
21406         }
21407         
21408         function cleanAttr(n,v)
21409         {
21410             
21411             if (v.match(/^\./) || v.match(/^\//)) {
21412                 return;
21413             }
21414             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21415                 return;
21416             }
21417             if (v.match(/^#/)) {
21418                 return;
21419             }
21420 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21421             node.removeAttribute(n);
21422             
21423         }
21424         
21425         var cwhite = this.cwhite;
21426         var cblack = this.cblack;
21427             
21428         function cleanStyle(n,v)
21429         {
21430             if (v.match(/expression/)) { //XSS?? should we even bother..
21431                 node.removeAttribute(n);
21432                 return;
21433             }
21434             
21435             var parts = v.split(/;/);
21436             var clean = [];
21437             
21438             Roo.each(parts, function(p) {
21439                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21440                 if (!p.length) {
21441                     return true;
21442                 }
21443                 var l = p.split(':').shift().replace(/\s+/g,'');
21444                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21445                 
21446                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21447 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21448                     //node.removeAttribute(n);
21449                     return true;
21450                 }
21451                 //Roo.log()
21452                 // only allow 'c whitelisted system attributes'
21453                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21454 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21455                     //node.removeAttribute(n);
21456                     return true;
21457                 }
21458                 
21459                 
21460                  
21461                 
21462                 clean.push(p);
21463                 return true;
21464             });
21465             if (clean.length) { 
21466                 node.setAttribute(n, clean.join(';'));
21467             } else {
21468                 node.removeAttribute(n);
21469             }
21470             
21471         }
21472         
21473         
21474         for (var i = node.attributes.length-1; i > -1 ; i--) {
21475             var a = node.attributes[i];
21476             //console.log(a);
21477             
21478             if (a.name.toLowerCase().substr(0,2)=='on')  {
21479                 node.removeAttribute(a.name);
21480                 continue;
21481             }
21482             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21483                 node.removeAttribute(a.name);
21484                 continue;
21485             }
21486             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21487                 cleanAttr(a.name,a.value); // fixme..
21488                 continue;
21489             }
21490             if (a.name == 'style') {
21491                 cleanStyle(a.name,a.value);
21492                 continue;
21493             }
21494             /// clean up MS crap..
21495             // tecnically this should be a list of valid class'es..
21496             
21497             
21498             if (a.name == 'class') {
21499                 if (a.value.match(/^Mso/)) {
21500                     node.className = '';
21501                 }
21502                 
21503                 if (a.value.match(/^body$/)) {
21504                     node.className = '';
21505                 }
21506                 continue;
21507             }
21508             
21509             // style cleanup!?
21510             // class cleanup?
21511             
21512         }
21513         
21514         
21515         this.cleanUpChildren(node);
21516         
21517         
21518     },
21519     
21520     /**
21521      * Clean up MS wordisms...
21522      */
21523     cleanWord : function(node)
21524     {
21525         
21526         
21527         if (!node) {
21528             this.cleanWord(this.doc.body);
21529             return;
21530         }
21531         if (node.nodeName == "#text") {
21532             // clean up silly Windows -- stuff?
21533             return; 
21534         }
21535         if (node.nodeName == "#comment") {
21536             node.parentNode.removeChild(node);
21537             // clean up silly Windows -- stuff?
21538             return; 
21539         }
21540         
21541         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21542             node.parentNode.removeChild(node);
21543             return;
21544         }
21545         
21546         // remove - but keep children..
21547         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21548             while (node.childNodes.length) {
21549                 var cn = node.childNodes[0];
21550                 node.removeChild(cn);
21551                 node.parentNode.insertBefore(cn, node);
21552             }
21553             node.parentNode.removeChild(node);
21554             this.iterateChildren(node, this.cleanWord);
21555             return;
21556         }
21557         // clean styles
21558         if (node.className.length) {
21559             
21560             var cn = node.className.split(/\W+/);
21561             var cna = [];
21562             Roo.each(cn, function(cls) {
21563                 if (cls.match(/Mso[a-zA-Z]+/)) {
21564                     return;
21565                 }
21566                 cna.push(cls);
21567             });
21568             node.className = cna.length ? cna.join(' ') : '';
21569             if (!cna.length) {
21570                 node.removeAttribute("class");
21571             }
21572         }
21573         
21574         if (node.hasAttribute("lang")) {
21575             node.removeAttribute("lang");
21576         }
21577         
21578         if (node.hasAttribute("style")) {
21579             
21580             var styles = node.getAttribute("style").split(";");
21581             var nstyle = [];
21582             Roo.each(styles, function(s) {
21583                 if (!s.match(/:/)) {
21584                     return;
21585                 }
21586                 var kv = s.split(":");
21587                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21588                     return;
21589                 }
21590                 // what ever is left... we allow.
21591                 nstyle.push(s);
21592             });
21593             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21594             if (!nstyle.length) {
21595                 node.removeAttribute('style');
21596             }
21597         }
21598         this.iterateChildren(node, this.cleanWord);
21599         
21600         
21601         
21602     },
21603     /**
21604      * iterateChildren of a Node, calling fn each time, using this as the scole..
21605      * @param {DomNode} node node to iterate children of.
21606      * @param {Function} fn method of this class to call on each item.
21607      */
21608     iterateChildren : function(node, fn)
21609     {
21610         if (!node.childNodes.length) {
21611                 return;
21612         }
21613         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21614            fn.call(this, node.childNodes[i])
21615         }
21616     },
21617     
21618     
21619     /**
21620      * cleanTableWidths.
21621      *
21622      * Quite often pasting from word etc.. results in tables with column and widths.
21623      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21624      *
21625      */
21626     cleanTableWidths : function(node)
21627     {
21628          
21629          
21630         if (!node) {
21631             this.cleanTableWidths(this.doc.body);
21632             return;
21633         }
21634         
21635         // ignore list...
21636         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21637             return; 
21638         }
21639         Roo.log(node.tagName);
21640         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21641             this.iterateChildren(node, this.cleanTableWidths);
21642             return;
21643         }
21644         if (node.hasAttribute('width')) {
21645             node.removeAttribute('width');
21646         }
21647         
21648          
21649         if (node.hasAttribute("style")) {
21650             // pretty basic...
21651             
21652             var styles = node.getAttribute("style").split(";");
21653             var nstyle = [];
21654             Roo.each(styles, function(s) {
21655                 if (!s.match(/:/)) {
21656                     return;
21657                 }
21658                 var kv = s.split(":");
21659                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21660                     return;
21661                 }
21662                 // what ever is left... we allow.
21663                 nstyle.push(s);
21664             });
21665             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21666             if (!nstyle.length) {
21667                 node.removeAttribute('style');
21668             }
21669         }
21670         
21671         this.iterateChildren(node, this.cleanTableWidths);
21672         
21673         
21674     },
21675     
21676     
21677     
21678     
21679     domToHTML : function(currentElement, depth, nopadtext) {
21680         
21681         depth = depth || 0;
21682         nopadtext = nopadtext || false;
21683     
21684         if (!currentElement) {
21685             return this.domToHTML(this.doc.body);
21686         }
21687         
21688         //Roo.log(currentElement);
21689         var j;
21690         var allText = false;
21691         var nodeName = currentElement.nodeName;
21692         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21693         
21694         if  (nodeName == '#text') {
21695             
21696             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21697         }
21698         
21699         
21700         var ret = '';
21701         if (nodeName != 'BODY') {
21702              
21703             var i = 0;
21704             // Prints the node tagName, such as <A>, <IMG>, etc
21705             if (tagName) {
21706                 var attr = [];
21707                 for(i = 0; i < currentElement.attributes.length;i++) {
21708                     // quoting?
21709                     var aname = currentElement.attributes.item(i).name;
21710                     if (!currentElement.attributes.item(i).value.length) {
21711                         continue;
21712                     }
21713                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21714                 }
21715                 
21716                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21717             } 
21718             else {
21719                 
21720                 // eack
21721             }
21722         } else {
21723             tagName = false;
21724         }
21725         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21726             return ret;
21727         }
21728         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21729             nopadtext = true;
21730         }
21731         
21732         
21733         // Traverse the tree
21734         i = 0;
21735         var currentElementChild = currentElement.childNodes.item(i);
21736         var allText = true;
21737         var innerHTML  = '';
21738         lastnode = '';
21739         while (currentElementChild) {
21740             // Formatting code (indent the tree so it looks nice on the screen)
21741             var nopad = nopadtext;
21742             if (lastnode == 'SPAN') {
21743                 nopad  = true;
21744             }
21745             // text
21746             if  (currentElementChild.nodeName == '#text') {
21747                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21748                 toadd = nopadtext ? toadd : toadd.trim();
21749                 if (!nopad && toadd.length > 80) {
21750                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21751                 }
21752                 innerHTML  += toadd;
21753                 
21754                 i++;
21755                 currentElementChild = currentElement.childNodes.item(i);
21756                 lastNode = '';
21757                 continue;
21758             }
21759             allText = false;
21760             
21761             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21762                 
21763             // Recursively traverse the tree structure of the child node
21764             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21765             lastnode = currentElementChild.nodeName;
21766             i++;
21767             currentElementChild=currentElement.childNodes.item(i);
21768         }
21769         
21770         ret += innerHTML;
21771         
21772         if (!allText) {
21773                 // The remaining code is mostly for formatting the tree
21774             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21775         }
21776         
21777         
21778         if (tagName) {
21779             ret+= "</"+tagName+">";
21780         }
21781         return ret;
21782         
21783     },
21784         
21785     applyBlacklists : function()
21786     {
21787         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21788         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21789         
21790         this.white = [];
21791         this.black = [];
21792         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21793             if (b.indexOf(tag) > -1) {
21794                 return;
21795             }
21796             this.white.push(tag);
21797             
21798         }, this);
21799         
21800         Roo.each(w, function(tag) {
21801             if (b.indexOf(tag) > -1) {
21802                 return;
21803             }
21804             if (this.white.indexOf(tag) > -1) {
21805                 return;
21806             }
21807             this.white.push(tag);
21808             
21809         }, this);
21810         
21811         
21812         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21813             if (w.indexOf(tag) > -1) {
21814                 return;
21815             }
21816             this.black.push(tag);
21817             
21818         }, this);
21819         
21820         Roo.each(b, function(tag) {
21821             if (w.indexOf(tag) > -1) {
21822                 return;
21823             }
21824             if (this.black.indexOf(tag) > -1) {
21825                 return;
21826             }
21827             this.black.push(tag);
21828             
21829         }, this);
21830         
21831         
21832         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21833         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21834         
21835         this.cwhite = [];
21836         this.cblack = [];
21837         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21838             if (b.indexOf(tag) > -1) {
21839                 return;
21840             }
21841             this.cwhite.push(tag);
21842             
21843         }, this);
21844         
21845         Roo.each(w, function(tag) {
21846             if (b.indexOf(tag) > -1) {
21847                 return;
21848             }
21849             if (this.cwhite.indexOf(tag) > -1) {
21850                 return;
21851             }
21852             this.cwhite.push(tag);
21853             
21854         }, this);
21855         
21856         
21857         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21858             if (w.indexOf(tag) > -1) {
21859                 return;
21860             }
21861             this.cblack.push(tag);
21862             
21863         }, this);
21864         
21865         Roo.each(b, function(tag) {
21866             if (w.indexOf(tag) > -1) {
21867                 return;
21868             }
21869             if (this.cblack.indexOf(tag) > -1) {
21870                 return;
21871             }
21872             this.cblack.push(tag);
21873             
21874         }, this);
21875     },
21876     
21877     setStylesheets : function(stylesheets)
21878     {
21879         if(typeof(stylesheets) == 'string'){
21880             Roo.get(this.iframe.contentDocument.head).createChild({
21881                 tag : 'link',
21882                 rel : 'stylesheet',
21883                 type : 'text/css',
21884                 href : stylesheets
21885             });
21886             
21887             return;
21888         }
21889         var _this = this;
21890      
21891         Roo.each(stylesheets, function(s) {
21892             if(!s.length){
21893                 return;
21894             }
21895             
21896             Roo.get(_this.iframe.contentDocument.head).createChild({
21897                 tag : 'link',
21898                 rel : 'stylesheet',
21899                 type : 'text/css',
21900                 href : s
21901             });
21902         });
21903
21904         
21905     },
21906     
21907     removeStylesheets : function()
21908     {
21909         var _this = this;
21910         
21911         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21912             s.remove();
21913         });
21914     },
21915     
21916     setStyle : function(style)
21917     {
21918         Roo.get(this.iframe.contentDocument.head).createChild({
21919             tag : 'style',
21920             type : 'text/css',
21921             html : style
21922         });
21923
21924         return;
21925     }
21926     
21927     // hide stuff that is not compatible
21928     /**
21929      * @event blur
21930      * @hide
21931      */
21932     /**
21933      * @event change
21934      * @hide
21935      */
21936     /**
21937      * @event focus
21938      * @hide
21939      */
21940     /**
21941      * @event specialkey
21942      * @hide
21943      */
21944     /**
21945      * @cfg {String} fieldClass @hide
21946      */
21947     /**
21948      * @cfg {String} focusClass @hide
21949      */
21950     /**
21951      * @cfg {String} autoCreate @hide
21952      */
21953     /**
21954      * @cfg {String} inputType @hide
21955      */
21956     /**
21957      * @cfg {String} invalidClass @hide
21958      */
21959     /**
21960      * @cfg {String} invalidText @hide
21961      */
21962     /**
21963      * @cfg {String} msgFx @hide
21964      */
21965     /**
21966      * @cfg {String} validateOnBlur @hide
21967      */
21968 });
21969
21970 Roo.HtmlEditorCore.white = [
21971         'area', 'br', 'img', 'input', 'hr', 'wbr',
21972         
21973        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21974        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21975        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21976        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21977        'table',   'ul',         'xmp', 
21978        
21979        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21980       'thead',   'tr', 
21981      
21982       'dir', 'menu', 'ol', 'ul', 'dl',
21983        
21984       'embed',  'object'
21985 ];
21986
21987
21988 Roo.HtmlEditorCore.black = [
21989     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21990         'applet', // 
21991         'base',   'basefont', 'bgsound', 'blink',  'body', 
21992         'frame',  'frameset', 'head',    'html',   'ilayer', 
21993         'iframe', 'layer',  'link',     'meta',    'object',   
21994         'script', 'style' ,'title',  'xml' // clean later..
21995 ];
21996 Roo.HtmlEditorCore.clean = [
21997     'script', 'style', 'title', 'xml'
21998 ];
21999 Roo.HtmlEditorCore.remove = [
22000     'font'
22001 ];
22002 // attributes..
22003
22004 Roo.HtmlEditorCore.ablack = [
22005     'on'
22006 ];
22007     
22008 Roo.HtmlEditorCore.aclean = [ 
22009     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22010 ];
22011
22012 // protocols..
22013 Roo.HtmlEditorCore.pwhite= [
22014         'http',  'https',  'mailto'
22015 ];
22016
22017 // white listed style attributes.
22018 Roo.HtmlEditorCore.cwhite= [
22019       //  'text-align', /// default is to allow most things..
22020       
22021          
22022 //        'font-size'//??
22023 ];
22024
22025 // black listed style attributes.
22026 Roo.HtmlEditorCore.cblack= [
22027       //  'font-size' -- this can be set by the project 
22028 ];
22029
22030
22031 Roo.HtmlEditorCore.swapCodes   =[ 
22032     [    8211, "--" ], 
22033     [    8212, "--" ], 
22034     [    8216,  "'" ],  
22035     [    8217, "'" ],  
22036     [    8220, '"' ],  
22037     [    8221, '"' ],  
22038     [    8226, "*" ],  
22039     [    8230, "..." ]
22040 ]; 
22041
22042     //<script type="text/javascript">
22043
22044 /*
22045  * Ext JS Library 1.1.1
22046  * Copyright(c) 2006-2007, Ext JS, LLC.
22047  * Licence LGPL
22048  * 
22049  */
22050  
22051  
22052 Roo.form.HtmlEditor = function(config){
22053     
22054     
22055     
22056     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22057     
22058     if (!this.toolbars) {
22059         this.toolbars = [];
22060     }
22061     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22062     
22063     
22064 };
22065
22066 /**
22067  * @class Roo.form.HtmlEditor
22068  * @extends Roo.form.Field
22069  * Provides a lightweight HTML Editor component.
22070  *
22071  * This has been tested on Fireforx / Chrome.. IE may not be so great..
22072  * 
22073  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22074  * supported by this editor.</b><br/><br/>
22075  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22076  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22077  */
22078 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22079     /**
22080      * @cfg {Boolean} clearUp
22081      */
22082     clearUp : true,
22083       /**
22084      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22085      */
22086     toolbars : false,
22087    
22088      /**
22089      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22090      *                        Roo.resizable.
22091      */
22092     resizable : false,
22093      /**
22094      * @cfg {Number} height (in pixels)
22095      */   
22096     height: 300,
22097    /**
22098      * @cfg {Number} width (in pixels)
22099      */   
22100     width: 500,
22101     
22102     /**
22103      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22104      * 
22105      */
22106     stylesheets: false,
22107     
22108     
22109      /**
22110      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22111      * 
22112      */
22113     cblack: false,
22114     /**
22115      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22116      * 
22117      */
22118     cwhite: false,
22119     
22120      /**
22121      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22122      * 
22123      */
22124     black: false,
22125     /**
22126      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22127      * 
22128      */
22129     white: false,
22130     
22131     // id of frame..
22132     frameId: false,
22133     
22134     // private properties
22135     validationEvent : false,
22136     deferHeight: true,
22137     initialized : false,
22138     activated : false,
22139     
22140     onFocus : Roo.emptyFn,
22141     iframePad:3,
22142     hideMode:'offsets',
22143     
22144     actionMode : 'container', // defaults to hiding it...
22145     
22146     defaultAutoCreate : { // modified by initCompnoent..
22147         tag: "textarea",
22148         style:"width:500px;height:300px;",
22149         autocomplete: "new-password"
22150     },
22151
22152     // private
22153     initComponent : function(){
22154         this.addEvents({
22155             /**
22156              * @event initialize
22157              * Fires when the editor is fully initialized (including the iframe)
22158              * @param {HtmlEditor} this
22159              */
22160             initialize: true,
22161             /**
22162              * @event activate
22163              * Fires when the editor is first receives the focus. Any insertion must wait
22164              * until after this event.
22165              * @param {HtmlEditor} this
22166              */
22167             activate: true,
22168              /**
22169              * @event beforesync
22170              * Fires before the textarea is updated with content from the editor iframe. Return false
22171              * to cancel the sync.
22172              * @param {HtmlEditor} this
22173              * @param {String} html
22174              */
22175             beforesync: true,
22176              /**
22177              * @event beforepush
22178              * Fires before the iframe editor is updated with content from the textarea. Return false
22179              * to cancel the push.
22180              * @param {HtmlEditor} this
22181              * @param {String} html
22182              */
22183             beforepush: true,
22184              /**
22185              * @event sync
22186              * Fires when the textarea is updated with content from the editor iframe.
22187              * @param {HtmlEditor} this
22188              * @param {String} html
22189              */
22190             sync: true,
22191              /**
22192              * @event push
22193              * Fires when the iframe editor is updated with content from the textarea.
22194              * @param {HtmlEditor} this
22195              * @param {String} html
22196              */
22197             push: true,
22198              /**
22199              * @event editmodechange
22200              * Fires when the editor switches edit modes
22201              * @param {HtmlEditor} this
22202              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22203              */
22204             editmodechange: true,
22205             /**
22206              * @event editorevent
22207              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22208              * @param {HtmlEditor} this
22209              */
22210             editorevent: true,
22211             /**
22212              * @event firstfocus
22213              * Fires when on first focus - needed by toolbars..
22214              * @param {HtmlEditor} this
22215              */
22216             firstfocus: true,
22217             /**
22218              * @event autosave
22219              * Auto save the htmlEditor value as a file into Events
22220              * @param {HtmlEditor} this
22221              */
22222             autosave: true,
22223             /**
22224              * @event savedpreview
22225              * preview the saved version of htmlEditor
22226              * @param {HtmlEditor} this
22227              */
22228             savedpreview: true,
22229             
22230             /**
22231             * @event stylesheetsclick
22232             * Fires when press the Sytlesheets button
22233             * @param {Roo.HtmlEditorCore} this
22234             */
22235             stylesheetsclick: true
22236         });
22237         this.defaultAutoCreate =  {
22238             tag: "textarea",
22239             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22240             autocomplete: "new-password"
22241         };
22242     },
22243
22244     /**
22245      * Protected method that will not generally be called directly. It
22246      * is called when the editor creates its toolbar. Override this method if you need to
22247      * add custom toolbar buttons.
22248      * @param {HtmlEditor} editor
22249      */
22250     createToolbar : function(editor){
22251         Roo.log("create toolbars");
22252         if (!editor.toolbars || !editor.toolbars.length) {
22253             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22254         }
22255         
22256         for (var i =0 ; i < editor.toolbars.length;i++) {
22257             editor.toolbars[i] = Roo.factory(
22258                     typeof(editor.toolbars[i]) == 'string' ?
22259                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
22260                 Roo.form.HtmlEditor);
22261             editor.toolbars[i].init(editor);
22262         }
22263          
22264         
22265     },
22266
22267      
22268     // private
22269     onRender : function(ct, position)
22270     {
22271         var _t = this;
22272         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22273         
22274         this.wrap = this.el.wrap({
22275             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22276         });
22277         
22278         this.editorcore.onRender(ct, position);
22279          
22280         if (this.resizable) {
22281             this.resizeEl = new Roo.Resizable(this.wrap, {
22282                 pinned : true,
22283                 wrap: true,
22284                 dynamic : true,
22285                 minHeight : this.height,
22286                 height: this.height,
22287                 handles : this.resizable,
22288                 width: this.width,
22289                 listeners : {
22290                     resize : function(r, w, h) {
22291                         _t.onResize(w,h); // -something
22292                     }
22293                 }
22294             });
22295             
22296         }
22297         this.createToolbar(this);
22298        
22299         
22300         if(!this.width){
22301             this.setSize(this.wrap.getSize());
22302         }
22303         if (this.resizeEl) {
22304             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22305             // should trigger onReize..
22306         }
22307         
22308         this.keyNav = new Roo.KeyNav(this.el, {
22309             
22310             "tab" : function(e){
22311                 e.preventDefault();
22312                 
22313                 var value = this.getValue();
22314                 
22315                 var start = this.el.dom.selectionStart;
22316                 var end = this.el.dom.selectionEnd;
22317                 
22318                 if(!e.shiftKey){
22319                     
22320                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22321                     this.el.dom.setSelectionRange(end + 1, end + 1);
22322                     return;
22323                 }
22324                 
22325                 var f = value.substring(0, start).split("\t");
22326                 
22327                 if(f.pop().length != 0){
22328                     return;
22329                 }
22330                 
22331                 this.setValue(f.join("\t") + value.substring(end));
22332                 this.el.dom.setSelectionRange(start - 1, start - 1);
22333                 
22334             },
22335             
22336             "home" : function(e){
22337                 e.preventDefault();
22338                 
22339                 var curr = this.el.dom.selectionStart;
22340                 var lines = this.getValue().split("\n");
22341                 
22342                 if(!lines.length){
22343                     return;
22344                 }
22345                 
22346                 if(e.ctrlKey){
22347                     this.el.dom.setSelectionRange(0, 0);
22348                     return;
22349                 }
22350                 
22351                 var pos = 0;
22352                 
22353                 for (var i = 0; i < lines.length;i++) {
22354                     pos += lines[i].length;
22355                     
22356                     if(i != 0){
22357                         pos += 1;
22358                     }
22359                     
22360                     if(pos < curr){
22361                         continue;
22362                     }
22363                     
22364                     pos -= lines[i].length;
22365                     
22366                     break;
22367                 }
22368                 
22369                 if(!e.shiftKey){
22370                     this.el.dom.setSelectionRange(pos, pos);
22371                     return;
22372                 }
22373                 
22374                 this.el.dom.selectionStart = pos;
22375                 this.el.dom.selectionEnd = curr;
22376             },
22377             
22378             "end" : function(e){
22379                 e.preventDefault();
22380                 
22381                 var curr = this.el.dom.selectionStart;
22382                 var lines = this.getValue().split("\n");
22383                 
22384                 if(!lines.length){
22385                     return;
22386                 }
22387                 
22388                 if(e.ctrlKey){
22389                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22390                     return;
22391                 }
22392                 
22393                 var pos = 0;
22394                 
22395                 for (var i = 0; i < lines.length;i++) {
22396                     
22397                     pos += lines[i].length;
22398                     
22399                     if(i != 0){
22400                         pos += 1;
22401                     }
22402                     
22403                     if(pos < curr){
22404                         continue;
22405                     }
22406                     
22407                     break;
22408                 }
22409                 
22410                 if(!e.shiftKey){
22411                     this.el.dom.setSelectionRange(pos, pos);
22412                     return;
22413                 }
22414                 
22415                 this.el.dom.selectionStart = curr;
22416                 this.el.dom.selectionEnd = pos;
22417             },
22418
22419             scope : this,
22420
22421             doRelay : function(foo, bar, hname){
22422                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22423             },
22424
22425             forceKeyDown: true
22426         });
22427         
22428 //        if(this.autosave && this.w){
22429 //            this.autoSaveFn = setInterval(this.autosave, 1000);
22430 //        }
22431     },
22432
22433     // private
22434     onResize : function(w, h)
22435     {
22436         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22437         var ew = false;
22438         var eh = false;
22439         
22440         if(this.el ){
22441             if(typeof w == 'number'){
22442                 var aw = w - this.wrap.getFrameWidth('lr');
22443                 this.el.setWidth(this.adjustWidth('textarea', aw));
22444                 ew = aw;
22445             }
22446             if(typeof h == 'number'){
22447                 var tbh = 0;
22448                 for (var i =0; i < this.toolbars.length;i++) {
22449                     // fixme - ask toolbars for heights?
22450                     tbh += this.toolbars[i].tb.el.getHeight();
22451                     if (this.toolbars[i].footer) {
22452                         tbh += this.toolbars[i].footer.el.getHeight();
22453                     }
22454                 }
22455                 
22456                 
22457                 
22458                 
22459                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22460                 ah -= 5; // knock a few pixes off for look..
22461 //                Roo.log(ah);
22462                 this.el.setHeight(this.adjustWidth('textarea', ah));
22463                 var eh = ah;
22464             }
22465         }
22466         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22467         this.editorcore.onResize(ew,eh);
22468         
22469     },
22470
22471     /**
22472      * Toggles the editor between standard and source edit mode.
22473      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22474      */
22475     toggleSourceEdit : function(sourceEditMode)
22476     {
22477         this.editorcore.toggleSourceEdit(sourceEditMode);
22478         
22479         if(this.editorcore.sourceEditMode){
22480             Roo.log('editor - showing textarea');
22481             
22482 //            Roo.log('in');
22483 //            Roo.log(this.syncValue());
22484             this.editorcore.syncValue();
22485             this.el.removeClass('x-hidden');
22486             this.el.dom.removeAttribute('tabIndex');
22487             this.el.focus();
22488             
22489             for (var i = 0; i < this.toolbars.length; i++) {
22490                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22491                     this.toolbars[i].tb.hide();
22492                     this.toolbars[i].footer.hide();
22493                 }
22494             }
22495             
22496         }else{
22497             Roo.log('editor - hiding textarea');
22498 //            Roo.log('out')
22499 //            Roo.log(this.pushValue()); 
22500             this.editorcore.pushValue();
22501             
22502             this.el.addClass('x-hidden');
22503             this.el.dom.setAttribute('tabIndex', -1);
22504             
22505             for (var i = 0; i < this.toolbars.length; i++) {
22506                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22507                     this.toolbars[i].tb.show();
22508                     this.toolbars[i].footer.show();
22509                 }
22510             }
22511             
22512             //this.deferFocus();
22513         }
22514         
22515         this.setSize(this.wrap.getSize());
22516         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
22517         
22518         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22519     },
22520  
22521     // private (for BoxComponent)
22522     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22523
22524     // private (for BoxComponent)
22525     getResizeEl : function(){
22526         return this.wrap;
22527     },
22528
22529     // private (for BoxComponent)
22530     getPositionEl : function(){
22531         return this.wrap;
22532     },
22533
22534     // private
22535     initEvents : function(){
22536         this.originalValue = this.getValue();
22537     },
22538
22539     /**
22540      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22541      * @method
22542      */
22543     markInvalid : Roo.emptyFn,
22544     /**
22545      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22546      * @method
22547      */
22548     clearInvalid : Roo.emptyFn,
22549
22550     setValue : function(v){
22551         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
22552         this.editorcore.pushValue();
22553     },
22554
22555      
22556     // private
22557     deferFocus : function(){
22558         this.focus.defer(10, this);
22559     },
22560
22561     // doc'ed in Field
22562     focus : function(){
22563         this.editorcore.focus();
22564         
22565     },
22566       
22567
22568     // private
22569     onDestroy : function(){
22570         
22571         
22572         
22573         if(this.rendered){
22574             
22575             for (var i =0; i < this.toolbars.length;i++) {
22576                 // fixme - ask toolbars for heights?
22577                 this.toolbars[i].onDestroy();
22578             }
22579             
22580             this.wrap.dom.innerHTML = '';
22581             this.wrap.remove();
22582         }
22583     },
22584
22585     // private
22586     onFirstFocus : function(){
22587         //Roo.log("onFirstFocus");
22588         this.editorcore.onFirstFocus();
22589          for (var i =0; i < this.toolbars.length;i++) {
22590             this.toolbars[i].onFirstFocus();
22591         }
22592         
22593     },
22594     
22595     // private
22596     syncValue : function()
22597     {
22598         this.editorcore.syncValue();
22599     },
22600     
22601     pushValue : function()
22602     {
22603         this.editorcore.pushValue();
22604     },
22605     
22606     setStylesheets : function(stylesheets)
22607     {
22608         this.editorcore.setStylesheets(stylesheets);
22609     },
22610     
22611     removeStylesheets : function()
22612     {
22613         this.editorcore.removeStylesheets();
22614     }
22615      
22616     
22617     // hide stuff that is not compatible
22618     /**
22619      * @event blur
22620      * @hide
22621      */
22622     /**
22623      * @event change
22624      * @hide
22625      */
22626     /**
22627      * @event focus
22628      * @hide
22629      */
22630     /**
22631      * @event specialkey
22632      * @hide
22633      */
22634     /**
22635      * @cfg {String} fieldClass @hide
22636      */
22637     /**
22638      * @cfg {String} focusClass @hide
22639      */
22640     /**
22641      * @cfg {String} autoCreate @hide
22642      */
22643     /**
22644      * @cfg {String} inputType @hide
22645      */
22646     /**
22647      * @cfg {String} invalidClass @hide
22648      */
22649     /**
22650      * @cfg {String} invalidText @hide
22651      */
22652     /**
22653      * @cfg {String} msgFx @hide
22654      */
22655     /**
22656      * @cfg {String} validateOnBlur @hide
22657      */
22658 });
22659  
22660     // <script type="text/javascript">
22661 /*
22662  * Based on
22663  * Ext JS Library 1.1.1
22664  * Copyright(c) 2006-2007, Ext JS, LLC.
22665  *  
22666  
22667  */
22668
22669 /**
22670  * @class Roo.form.HtmlEditorToolbar1
22671  * Basic Toolbar
22672  * 
22673  * Usage:
22674  *
22675  new Roo.form.HtmlEditor({
22676     ....
22677     toolbars : [
22678         new Roo.form.HtmlEditorToolbar1({
22679             disable : { fonts: 1 , format: 1, ..., ... , ...],
22680             btns : [ .... ]
22681         })
22682     }
22683      
22684  * 
22685  * @cfg {Object} disable List of elements to disable..
22686  * @cfg {Array} btns List of additional buttons.
22687  * 
22688  * 
22689  * NEEDS Extra CSS? 
22690  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22691  */
22692  
22693 Roo.form.HtmlEditor.ToolbarStandard = function(config)
22694 {
22695     
22696     Roo.apply(this, config);
22697     
22698     // default disabled, based on 'good practice'..
22699     this.disable = this.disable || {};
22700     Roo.applyIf(this.disable, {
22701         fontSize : true,
22702         colors : true,
22703         specialElements : true
22704     });
22705     
22706     
22707     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22708     // dont call parent... till later.
22709 }
22710
22711 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
22712     
22713     tb: false,
22714     
22715     rendered: false,
22716     
22717     editor : false,
22718     editorcore : false,
22719     /**
22720      * @cfg {Object} disable  List of toolbar elements to disable
22721          
22722      */
22723     disable : false,
22724     
22725     
22726      /**
22727      * @cfg {String} createLinkText The default text for the create link prompt
22728      */
22729     createLinkText : 'Please enter the URL for the link:',
22730     /**
22731      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
22732      */
22733     defaultLinkValue : 'http:/'+'/',
22734    
22735     
22736       /**
22737      * @cfg {Array} fontFamilies An array of available font families
22738      */
22739     fontFamilies : [
22740         'Arial',
22741         'Courier New',
22742         'Tahoma',
22743         'Times New Roman',
22744         'Verdana'
22745     ],
22746     
22747     specialChars : [
22748            "&#169;",
22749           "&#174;",     
22750           "&#8482;",    
22751           "&#163;" ,    
22752          // "&#8212;",    
22753           "&#8230;",    
22754           "&#247;" ,    
22755         //  "&#225;" ,     ?? a acute?
22756            "&#8364;"    , //Euro
22757        //   "&#8220;"    ,
22758         //  "&#8221;"    ,
22759         //  "&#8226;"    ,
22760           "&#176;"  //   , // degrees
22761
22762          // "&#233;"     , // e ecute
22763          // "&#250;"     , // u ecute?
22764     ],
22765     
22766     specialElements : [
22767         {
22768             text: "Insert Table",
22769             xtype: 'MenuItem',
22770             xns : Roo.Menu,
22771             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
22772                 
22773         },
22774         {    
22775             text: "Insert Image",
22776             xtype: 'MenuItem',
22777             xns : Roo.Menu,
22778             ihtml : '<img src="about:blank"/>'
22779             
22780         }
22781         
22782          
22783     ],
22784     
22785     
22786     inputElements : [ 
22787             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
22788             "input:submit", "input:button", "select", "textarea", "label" ],
22789     formats : [
22790         ["p"] ,  
22791         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
22792         ["pre"],[ "code"], 
22793         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
22794         ['div'],['span']
22795     ],
22796     
22797     cleanStyles : [
22798         "font-size"
22799     ],
22800      /**
22801      * @cfg {String} defaultFont default font to use.
22802      */
22803     defaultFont: 'tahoma',
22804    
22805     fontSelect : false,
22806     
22807     
22808     formatCombo : false,
22809     
22810     init : function(editor)
22811     {
22812         this.editor = editor;
22813         this.editorcore = editor.editorcore ? editor.editorcore : editor;
22814         var editorcore = this.editorcore;
22815         
22816         var _t = this;
22817         
22818         var fid = editorcore.frameId;
22819         var etb = this;
22820         function btn(id, toggle, handler){
22821             var xid = fid + '-'+ id ;
22822             return {
22823                 id : xid,
22824                 cmd : id,
22825                 cls : 'x-btn-icon x-edit-'+id,
22826                 enableToggle:toggle !== false,
22827                 scope: _t, // was editor...
22828                 handler:handler||_t.relayBtnCmd,
22829                 clickEvent:'mousedown',
22830                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
22831                 tabIndex:-1
22832             };
22833         }
22834         
22835         
22836         
22837         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
22838         this.tb = tb;
22839          // stop form submits
22840         tb.el.on('click', function(e){
22841             e.preventDefault(); // what does this do?
22842         });
22843
22844         if(!this.disable.font) { // && !Roo.isSafari){
22845             /* why no safari for fonts 
22846             editor.fontSelect = tb.el.createChild({
22847                 tag:'select',
22848                 tabIndex: -1,
22849                 cls:'x-font-select',
22850                 html: this.createFontOptions()
22851             });
22852             
22853             editor.fontSelect.on('change', function(){
22854                 var font = editor.fontSelect.dom.value;
22855                 editor.relayCmd('fontname', font);
22856                 editor.deferFocus();
22857             }, editor);
22858             
22859             tb.add(
22860                 editor.fontSelect.dom,
22861                 '-'
22862             );
22863             */
22864             
22865         };
22866         if(!this.disable.formats){
22867             this.formatCombo = new Roo.form.ComboBox({
22868                 store: new Roo.data.SimpleStore({
22869                     id : 'tag',
22870                     fields: ['tag'],
22871                     data : this.formats // from states.js
22872                 }),
22873                 blockFocus : true,
22874                 name : '',
22875                 //autoCreate : {tag: "div",  size: "20"},
22876                 displayField:'tag',
22877                 typeAhead: false,
22878                 mode: 'local',
22879                 editable : false,
22880                 triggerAction: 'all',
22881                 emptyText:'Add tag',
22882                 selectOnFocus:true,
22883                 width:135,
22884                 listeners : {
22885                     'select': function(c, r, i) {
22886                         editorcore.insertTag(r.get('tag'));
22887                         editor.focus();
22888                     }
22889                 }
22890
22891             });
22892             tb.addField(this.formatCombo);
22893             
22894         }
22895         
22896         if(!this.disable.format){
22897             tb.add(
22898                 btn('bold'),
22899                 btn('italic'),
22900                 btn('underline'),
22901                 btn('strikethrough')
22902             );
22903         };
22904         if(!this.disable.fontSize){
22905             tb.add(
22906                 '-',
22907                 
22908                 
22909                 btn('increasefontsize', false, editorcore.adjustFont),
22910                 btn('decreasefontsize', false, editorcore.adjustFont)
22911             );
22912         };
22913         
22914         
22915         if(!this.disable.colors){
22916             tb.add(
22917                 '-', {
22918                     id:editorcore.frameId +'-forecolor',
22919                     cls:'x-btn-icon x-edit-forecolor',
22920                     clickEvent:'mousedown',
22921                     tooltip: this.buttonTips['forecolor'] || undefined,
22922                     tabIndex:-1,
22923                     menu : new Roo.menu.ColorMenu({
22924                         allowReselect: true,
22925                         focus: Roo.emptyFn,
22926                         value:'000000',
22927                         plain:true,
22928                         selectHandler: function(cp, color){
22929                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
22930                             editor.deferFocus();
22931                         },
22932                         scope: editorcore,
22933                         clickEvent:'mousedown'
22934                     })
22935                 }, {
22936                     id:editorcore.frameId +'backcolor',
22937                     cls:'x-btn-icon x-edit-backcolor',
22938                     clickEvent:'mousedown',
22939                     tooltip: this.buttonTips['backcolor'] || undefined,
22940                     tabIndex:-1,
22941                     menu : new Roo.menu.ColorMenu({
22942                         focus: Roo.emptyFn,
22943                         value:'FFFFFF',
22944                         plain:true,
22945                         allowReselect: true,
22946                         selectHandler: function(cp, color){
22947                             if(Roo.isGecko){
22948                                 editorcore.execCmd('useCSS', false);
22949                                 editorcore.execCmd('hilitecolor', color);
22950                                 editorcore.execCmd('useCSS', true);
22951                                 editor.deferFocus();
22952                             }else{
22953                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
22954                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
22955                                 editor.deferFocus();
22956                             }
22957                         },
22958                         scope:editorcore,
22959                         clickEvent:'mousedown'
22960                     })
22961                 }
22962             );
22963         };
22964         // now add all the items...
22965         
22966
22967         if(!this.disable.alignments){
22968             tb.add(
22969                 '-',
22970                 btn('justifyleft'),
22971                 btn('justifycenter'),
22972                 btn('justifyright')
22973             );
22974         };
22975
22976         //if(!Roo.isSafari){
22977             if(!this.disable.links){
22978                 tb.add(
22979                     '-',
22980                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
22981                 );
22982             };
22983
22984             if(!this.disable.lists){
22985                 tb.add(
22986                     '-',
22987                     btn('insertorderedlist'),
22988                     btn('insertunorderedlist')
22989                 );
22990             }
22991             if(!this.disable.sourceEdit){
22992                 tb.add(
22993                     '-',
22994                     btn('sourceedit', true, function(btn){
22995                         this.toggleSourceEdit(btn.pressed);
22996                     })
22997                 );
22998             }
22999         //}
23000         
23001         var smenu = { };
23002         // special menu.. - needs to be tidied up..
23003         if (!this.disable.special) {
23004             smenu = {
23005                 text: "&#169;",
23006                 cls: 'x-edit-none',
23007                 
23008                 menu : {
23009                     items : []
23010                 }
23011             };
23012             for (var i =0; i < this.specialChars.length; i++) {
23013                 smenu.menu.items.push({
23014                     
23015                     html: this.specialChars[i],
23016                     handler: function(a,b) {
23017                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23018                         //editor.insertAtCursor(a.html);
23019                         
23020                     },
23021                     tabIndex:-1
23022                 });
23023             }
23024             
23025             
23026             tb.add(smenu);
23027             
23028             
23029         }
23030         
23031         var cmenu = { };
23032         if (!this.disable.cleanStyles) {
23033             cmenu = {
23034                 cls: 'x-btn-icon x-btn-clear',
23035                 
23036                 menu : {
23037                     items : []
23038                 }
23039             };
23040             for (var i =0; i < this.cleanStyles.length; i++) {
23041                 cmenu.menu.items.push({
23042                     actiontype : this.cleanStyles[i],
23043                     html: 'Remove ' + this.cleanStyles[i],
23044                     handler: function(a,b) {
23045 //                        Roo.log(a);
23046 //                        Roo.log(b);
23047                         var c = Roo.get(editorcore.doc.body);
23048                         c.select('[style]').each(function(s) {
23049                             s.dom.style.removeProperty(a.actiontype);
23050                         });
23051                         editorcore.syncValue();
23052                     },
23053                     tabIndex:-1
23054                 });
23055             }
23056              cmenu.menu.items.push({
23057                 actiontype : 'tablewidths',
23058                 html: 'Remove Table Widths',
23059                 handler: function(a,b) {
23060                     editorcore.cleanTableWidths();
23061                     editorcore.syncValue();
23062                 },
23063                 tabIndex:-1
23064             });
23065             cmenu.menu.items.push({
23066                 actiontype : 'word',
23067                 html: 'Remove MS Word Formating',
23068                 handler: function(a,b) {
23069                     editorcore.cleanWord();
23070                     editorcore.syncValue();
23071                 },
23072                 tabIndex:-1
23073             });
23074             
23075             cmenu.menu.items.push({
23076                 actiontype : 'all',
23077                 html: 'Remove All Styles',
23078                 handler: function(a,b) {
23079                     
23080                     var c = Roo.get(editorcore.doc.body);
23081                     c.select('[style]').each(function(s) {
23082                         s.dom.removeAttribute('style');
23083                     });
23084                     editorcore.syncValue();
23085                 },
23086                 tabIndex:-1
23087             });
23088             
23089             cmenu.menu.items.push({
23090                 actiontype : 'all',
23091                 html: 'Remove All CSS Classes',
23092                 handler: function(a,b) {
23093                     
23094                     var c = Roo.get(editorcore.doc.body);
23095                     c.select('[class]').each(function(s) {
23096                         s.dom.className = '';
23097                     });
23098                     editorcore.syncValue();
23099                 },
23100                 tabIndex:-1
23101             });
23102             
23103              cmenu.menu.items.push({
23104                 actiontype : 'tidy',
23105                 html: 'Tidy HTML Source',
23106                 handler: function(a,b) {
23107                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
23108                     editorcore.syncValue();
23109                 },
23110                 tabIndex:-1
23111             });
23112             
23113             
23114             tb.add(cmenu);
23115         }
23116          
23117         if (!this.disable.specialElements) {
23118             var semenu = {
23119                 text: "Other;",
23120                 cls: 'x-edit-none',
23121                 menu : {
23122                     items : []
23123                 }
23124             };
23125             for (var i =0; i < this.specialElements.length; i++) {
23126                 semenu.menu.items.push(
23127                     Roo.apply({ 
23128                         handler: function(a,b) {
23129                             editor.insertAtCursor(this.ihtml);
23130                         }
23131                     }, this.specialElements[i])
23132                 );
23133                     
23134             }
23135             
23136             tb.add(semenu);
23137             
23138             
23139         }
23140          
23141         
23142         if (this.btns) {
23143             for(var i =0; i< this.btns.length;i++) {
23144                 var b = Roo.factory(this.btns[i],Roo.form);
23145                 b.cls =  'x-edit-none';
23146                 
23147                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23148                     b.cls += ' x-init-enable';
23149                 }
23150                 
23151                 b.scope = editorcore;
23152                 tb.add(b);
23153             }
23154         
23155         }
23156         
23157         
23158         
23159         // disable everything...
23160         
23161         this.tb.items.each(function(item){
23162             
23163            if(
23164                 item.id != editorcore.frameId+ '-sourceedit' && 
23165                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23166             ){
23167                 
23168                 item.disable();
23169             }
23170         });
23171         this.rendered = true;
23172         
23173         // the all the btns;
23174         editor.on('editorevent', this.updateToolbar, this);
23175         // other toolbars need to implement this..
23176         //editor.on('editmodechange', this.updateToolbar, this);
23177     },
23178     
23179     
23180     relayBtnCmd : function(btn) {
23181         this.editorcore.relayCmd(btn.cmd);
23182     },
23183     // private used internally
23184     createLink : function(){
23185         Roo.log("create link?");
23186         var url = prompt(this.createLinkText, this.defaultLinkValue);
23187         if(url && url != 'http:/'+'/'){
23188             this.editorcore.relayCmd('createlink', url);
23189         }
23190     },
23191
23192     
23193     /**
23194      * Protected method that will not generally be called directly. It triggers
23195      * a toolbar update by reading the markup state of the current selection in the editor.
23196      */
23197     updateToolbar: function(){
23198
23199         if(!this.editorcore.activated){
23200             this.editor.onFirstFocus();
23201             return;
23202         }
23203
23204         var btns = this.tb.items.map, 
23205             doc = this.editorcore.doc,
23206             frameId = this.editorcore.frameId;
23207
23208         if(!this.disable.font && !Roo.isSafari){
23209             /*
23210             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23211             if(name != this.fontSelect.dom.value){
23212                 this.fontSelect.dom.value = name;
23213             }
23214             */
23215         }
23216         if(!this.disable.format){
23217             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23218             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23219             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23220             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23221         }
23222         if(!this.disable.alignments){
23223             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23224             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23225             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23226         }
23227         if(!Roo.isSafari && !this.disable.lists){
23228             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23229             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23230         }
23231         
23232         var ans = this.editorcore.getAllAncestors();
23233         if (this.formatCombo) {
23234             
23235             
23236             var store = this.formatCombo.store;
23237             this.formatCombo.setValue("");
23238             for (var i =0; i < ans.length;i++) {
23239                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23240                     // select it..
23241                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23242                     break;
23243                 }
23244             }
23245         }
23246         
23247         
23248         
23249         // hides menus... - so this cant be on a menu...
23250         Roo.menu.MenuMgr.hideAll();
23251
23252         //this.editorsyncValue();
23253     },
23254    
23255     
23256     createFontOptions : function(){
23257         var buf = [], fs = this.fontFamilies, ff, lc;
23258         
23259         
23260         
23261         for(var i = 0, len = fs.length; i< len; i++){
23262             ff = fs[i];
23263             lc = ff.toLowerCase();
23264             buf.push(
23265                 '<option value="',lc,'" style="font-family:',ff,';"',
23266                     (this.defaultFont == lc ? ' selected="true">' : '>'),
23267                     ff,
23268                 '</option>'
23269             );
23270         }
23271         return buf.join('');
23272     },
23273     
23274     toggleSourceEdit : function(sourceEditMode){
23275         
23276         Roo.log("toolbar toogle");
23277         if(sourceEditMode === undefined){
23278             sourceEditMode = !this.sourceEditMode;
23279         }
23280         this.sourceEditMode = sourceEditMode === true;
23281         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23282         // just toggle the button?
23283         if(btn.pressed !== this.sourceEditMode){
23284             btn.toggle(this.sourceEditMode);
23285             return;
23286         }
23287         
23288         if(sourceEditMode){
23289             Roo.log("disabling buttons");
23290             this.tb.items.each(function(item){
23291                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23292                     item.disable();
23293                 }
23294             });
23295           
23296         }else{
23297             Roo.log("enabling buttons");
23298             if(this.editorcore.initialized){
23299                 this.tb.items.each(function(item){
23300                     item.enable();
23301                 });
23302             }
23303             
23304         }
23305         Roo.log("calling toggole on editor");
23306         // tell the editor that it's been pressed..
23307         this.editor.toggleSourceEdit(sourceEditMode);
23308        
23309     },
23310      /**
23311      * Object collection of toolbar tooltips for the buttons in the editor. The key
23312      * is the command id associated with that button and the value is a valid QuickTips object.
23313      * For example:
23314 <pre><code>
23315 {
23316     bold : {
23317         title: 'Bold (Ctrl+B)',
23318         text: 'Make the selected text bold.',
23319         cls: 'x-html-editor-tip'
23320     },
23321     italic : {
23322         title: 'Italic (Ctrl+I)',
23323         text: 'Make the selected text italic.',
23324         cls: 'x-html-editor-tip'
23325     },
23326     ...
23327 </code></pre>
23328     * @type Object
23329      */
23330     buttonTips : {
23331         bold : {
23332             title: 'Bold (Ctrl+B)',
23333             text: 'Make the selected text bold.',
23334             cls: 'x-html-editor-tip'
23335         },
23336         italic : {
23337             title: 'Italic (Ctrl+I)',
23338             text: 'Make the selected text italic.',
23339             cls: 'x-html-editor-tip'
23340         },
23341         underline : {
23342             title: 'Underline (Ctrl+U)',
23343             text: 'Underline the selected text.',
23344             cls: 'x-html-editor-tip'
23345         },
23346         strikethrough : {
23347             title: 'Strikethrough',
23348             text: 'Strikethrough the selected text.',
23349             cls: 'x-html-editor-tip'
23350         },
23351         increasefontsize : {
23352             title: 'Grow Text',
23353             text: 'Increase the font size.',
23354             cls: 'x-html-editor-tip'
23355         },
23356         decreasefontsize : {
23357             title: 'Shrink Text',
23358             text: 'Decrease the font size.',
23359             cls: 'x-html-editor-tip'
23360         },
23361         backcolor : {
23362             title: 'Text Highlight Color',
23363             text: 'Change the background color of the selected text.',
23364             cls: 'x-html-editor-tip'
23365         },
23366         forecolor : {
23367             title: 'Font Color',
23368             text: 'Change the color of the selected text.',
23369             cls: 'x-html-editor-tip'
23370         },
23371         justifyleft : {
23372             title: 'Align Text Left',
23373             text: 'Align text to the left.',
23374             cls: 'x-html-editor-tip'
23375         },
23376         justifycenter : {
23377             title: 'Center Text',
23378             text: 'Center text in the editor.',
23379             cls: 'x-html-editor-tip'
23380         },
23381         justifyright : {
23382             title: 'Align Text Right',
23383             text: 'Align text to the right.',
23384             cls: 'x-html-editor-tip'
23385         },
23386         insertunorderedlist : {
23387             title: 'Bullet List',
23388             text: 'Start a bulleted list.',
23389             cls: 'x-html-editor-tip'
23390         },
23391         insertorderedlist : {
23392             title: 'Numbered List',
23393             text: 'Start a numbered list.',
23394             cls: 'x-html-editor-tip'
23395         },
23396         createlink : {
23397             title: 'Hyperlink',
23398             text: 'Make the selected text a hyperlink.',
23399             cls: 'x-html-editor-tip'
23400         },
23401         sourceedit : {
23402             title: 'Source Edit',
23403             text: 'Switch to source editing mode.',
23404             cls: 'x-html-editor-tip'
23405         }
23406     },
23407     // private
23408     onDestroy : function(){
23409         if(this.rendered){
23410             
23411             this.tb.items.each(function(item){
23412                 if(item.menu){
23413                     item.menu.removeAll();
23414                     if(item.menu.el){
23415                         item.menu.el.destroy();
23416                     }
23417                 }
23418                 item.destroy();
23419             });
23420              
23421         }
23422     },
23423     onFirstFocus: function() {
23424         this.tb.items.each(function(item){
23425            item.enable();
23426         });
23427     }
23428 });
23429
23430
23431
23432
23433 // <script type="text/javascript">
23434 /*
23435  * Based on
23436  * Ext JS Library 1.1.1
23437  * Copyright(c) 2006-2007, Ext JS, LLC.
23438  *  
23439  
23440  */
23441
23442  
23443 /**
23444  * @class Roo.form.HtmlEditor.ToolbarContext
23445  * Context Toolbar
23446  * 
23447  * Usage:
23448  *
23449  new Roo.form.HtmlEditor({
23450     ....
23451     toolbars : [
23452         { xtype: 'ToolbarStandard', styles : {} }
23453         { xtype: 'ToolbarContext', disable : {} }
23454     ]
23455 })
23456
23457      
23458  * 
23459  * @config : {Object} disable List of elements to disable.. (not done yet.)
23460  * @config : {Object} styles  Map of styles available.
23461  * 
23462  */
23463
23464 Roo.form.HtmlEditor.ToolbarContext = function(config)
23465 {
23466     
23467     Roo.apply(this, config);
23468     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23469     // dont call parent... till later.
23470     this.styles = this.styles || {};
23471 }
23472
23473  
23474
23475 Roo.form.HtmlEditor.ToolbarContext.types = {
23476     'IMG' : {
23477         width : {
23478             title: "Width",
23479             width: 40
23480         },
23481         height:  {
23482             title: "Height",
23483             width: 40
23484         },
23485         align: {
23486             title: "Align",
23487             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
23488             width : 80
23489             
23490         },
23491         border: {
23492             title: "Border",
23493             width: 40
23494         },
23495         alt: {
23496             title: "Alt",
23497             width: 120
23498         },
23499         src : {
23500             title: "Src",
23501             width: 220
23502         }
23503         
23504     },
23505     'A' : {
23506         name : {
23507             title: "Name",
23508             width: 50
23509         },
23510         target:  {
23511             title: "Target",
23512             width: 120
23513         },
23514         href:  {
23515             title: "Href",
23516             width: 220
23517         } // border?
23518         
23519     },
23520     'TABLE' : {
23521         rows : {
23522             title: "Rows",
23523             width: 20
23524         },
23525         cols : {
23526             title: "Cols",
23527             width: 20
23528         },
23529         width : {
23530             title: "Width",
23531             width: 40
23532         },
23533         height : {
23534             title: "Height",
23535             width: 40
23536         },
23537         border : {
23538             title: "Border",
23539             width: 20
23540         }
23541     },
23542     'TD' : {
23543         width : {
23544             title: "Width",
23545             width: 40
23546         },
23547         height : {
23548             title: "Height",
23549             width: 40
23550         },   
23551         align: {
23552             title: "Align",
23553             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
23554             width: 80
23555         },
23556         valign: {
23557             title: "Valign",
23558             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
23559             width: 80
23560         },
23561         colspan: {
23562             title: "Colspan",
23563             width: 20
23564             
23565         },
23566          'font-family'  : {
23567             title : "Font",
23568             style : 'fontFamily',
23569             displayField: 'display',
23570             optname : 'font-family',
23571             width: 140
23572         }
23573     },
23574     'INPUT' : {
23575         name : {
23576             title: "name",
23577             width: 120
23578         },
23579         value : {
23580             title: "Value",
23581             width: 120
23582         },
23583         width : {
23584             title: "Width",
23585             width: 40
23586         }
23587     },
23588     'LABEL' : {
23589         'for' : {
23590             title: "For",
23591             width: 120
23592         }
23593     },
23594     'TEXTAREA' : {
23595           name : {
23596             title: "name",
23597             width: 120
23598         },
23599         rows : {
23600             title: "Rows",
23601             width: 20
23602         },
23603         cols : {
23604             title: "Cols",
23605             width: 20
23606         }
23607     },
23608     'SELECT' : {
23609         name : {
23610             title: "name",
23611             width: 120
23612         },
23613         selectoptions : {
23614             title: "Options",
23615             width: 200
23616         }
23617     },
23618     
23619     // should we really allow this??
23620     // should this just be 
23621     'BODY' : {
23622         title : {
23623             title: "Title",
23624             width: 200,
23625             disabled : true
23626         }
23627     },
23628     'SPAN' : {
23629         'font-family'  : {
23630             title : "Font",
23631             style : 'fontFamily',
23632             displayField: 'display',
23633             optname : 'font-family',
23634             width: 140
23635         }
23636     },
23637     'DIV' : {
23638         'font-family'  : {
23639             title : "Font",
23640             style : 'fontFamily',
23641             displayField: 'display',
23642             optname : 'font-family',
23643             width: 140
23644         }
23645     },
23646      'P' : {
23647         'font-family'  : {
23648             title : "Font",
23649             style : 'fontFamily',
23650             displayField: 'display',
23651             optname : 'font-family',
23652             width: 140
23653         }
23654     },
23655     
23656     '*' : {
23657         // empty..
23658     }
23659
23660 };
23661
23662 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
23663 Roo.form.HtmlEditor.ToolbarContext.stores = false;
23664
23665 Roo.form.HtmlEditor.ToolbarContext.options = {
23666         'font-family'  : [ 
23667                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
23668                 [ 'Courier New', 'Courier New'],
23669                 [ 'Tahoma', 'Tahoma'],
23670                 [ 'Times New Roman,serif', 'Times'],
23671                 [ 'Verdana','Verdana' ]
23672         ]
23673 };
23674
23675 // fixme - these need to be configurable..
23676  
23677
23678 //Roo.form.HtmlEditor.ToolbarContext.types
23679
23680
23681 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
23682     
23683     tb: false,
23684     
23685     rendered: false,
23686     
23687     editor : false,
23688     editorcore : false,
23689     /**
23690      * @cfg {Object} disable  List of toolbar elements to disable
23691          
23692      */
23693     disable : false,
23694     /**
23695      * @cfg {Object} styles List of styles 
23696      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
23697      *
23698      * These must be defined in the page, so they get rendered correctly..
23699      * .headline { }
23700      * TD.underline { }
23701      * 
23702      */
23703     styles : false,
23704     
23705     options: false,
23706     
23707     toolbars : false,
23708     
23709     init : function(editor)
23710     {
23711         this.editor = editor;
23712         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23713         var editorcore = this.editorcore;
23714         
23715         var fid = editorcore.frameId;
23716         var etb = this;
23717         function btn(id, toggle, handler){
23718             var xid = fid + '-'+ id ;
23719             return {
23720                 id : xid,
23721                 cmd : id,
23722                 cls : 'x-btn-icon x-edit-'+id,
23723                 enableToggle:toggle !== false,
23724                 scope: editorcore, // was editor...
23725                 handler:handler||editorcore.relayBtnCmd,
23726                 clickEvent:'mousedown',
23727                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23728                 tabIndex:-1
23729             };
23730         }
23731         // create a new element.
23732         var wdiv = editor.wrap.createChild({
23733                 tag: 'div'
23734             }, editor.wrap.dom.firstChild.nextSibling, true);
23735         
23736         // can we do this more than once??
23737         
23738          // stop form submits
23739       
23740  
23741         // disable everything...
23742         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23743         this.toolbars = {};
23744            
23745         for (var i in  ty) {
23746           
23747             this.toolbars[i] = this.buildToolbar(ty[i],i);
23748         }
23749         this.tb = this.toolbars.BODY;
23750         this.tb.el.show();
23751         this.buildFooter();
23752         this.footer.show();
23753         editor.on('hide', function( ) { this.footer.hide() }, this);
23754         editor.on('show', function( ) { this.footer.show() }, this);
23755         
23756          
23757         this.rendered = true;
23758         
23759         // the all the btns;
23760         editor.on('editorevent', this.updateToolbar, this);
23761         // other toolbars need to implement this..
23762         //editor.on('editmodechange', this.updateToolbar, this);
23763     },
23764     
23765     
23766     
23767     /**
23768      * Protected method that will not generally be called directly. It triggers
23769      * a toolbar update by reading the markup state of the current selection in the editor.
23770      *
23771      * Note you can force an update by calling on('editorevent', scope, false)
23772      */
23773     updateToolbar: function(editor,ev,sel){
23774
23775         //Roo.log(ev);
23776         // capture mouse up - this is handy for selecting images..
23777         // perhaps should go somewhere else...
23778         if(!this.editorcore.activated){
23779              this.editor.onFirstFocus();
23780             return;
23781         }
23782         
23783         
23784         
23785         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
23786         // selectNode - might want to handle IE?
23787         if (ev &&
23788             (ev.type == 'mouseup' || ev.type == 'click' ) &&
23789             ev.target && ev.target.tagName == 'IMG') {
23790             // they have click on an image...
23791             // let's see if we can change the selection...
23792             sel = ev.target;
23793          
23794               var nodeRange = sel.ownerDocument.createRange();
23795             try {
23796                 nodeRange.selectNode(sel);
23797             } catch (e) {
23798                 nodeRange.selectNodeContents(sel);
23799             }
23800             //nodeRange.collapse(true);
23801             var s = this.editorcore.win.getSelection();
23802             s.removeAllRanges();
23803             s.addRange(nodeRange);
23804         }  
23805         
23806       
23807         var updateFooter = sel ? false : true;
23808         
23809         
23810         var ans = this.editorcore.getAllAncestors();
23811         
23812         // pick
23813         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23814         
23815         if (!sel) { 
23816             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
23817             sel = sel ? sel : this.editorcore.doc.body;
23818             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
23819             
23820         }
23821         // pick a menu that exists..
23822         var tn = sel.tagName.toUpperCase();
23823         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
23824         
23825         tn = sel.tagName.toUpperCase();
23826         
23827         var lastSel = this.tb.selectedNode;
23828         
23829         this.tb.selectedNode = sel;
23830         
23831         // if current menu does not match..
23832         
23833         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
23834                 
23835             this.tb.el.hide();
23836             ///console.log("show: " + tn);
23837             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
23838             this.tb.el.show();
23839             // update name
23840             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
23841             
23842             
23843             // update attributes
23844             if (this.tb.fields) {
23845                 this.tb.fields.each(function(e) {
23846                     if (e.stylename) {
23847                         e.setValue(sel.style[e.stylename]);
23848                         return;
23849                     } 
23850                    e.setValue(sel.getAttribute(e.attrname));
23851                 });
23852             }
23853             
23854             var hasStyles = false;
23855             for(var i in this.styles) {
23856                 hasStyles = true;
23857                 break;
23858             }
23859             
23860             // update styles
23861             if (hasStyles) { 
23862                 var st = this.tb.fields.item(0);
23863                 
23864                 st.store.removeAll();
23865                
23866                 
23867                 var cn = sel.className.split(/\s+/);
23868                 
23869                 var avs = [];
23870                 if (this.styles['*']) {
23871                     
23872                     Roo.each(this.styles['*'], function(v) {
23873                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
23874                     });
23875                 }
23876                 if (this.styles[tn]) { 
23877                     Roo.each(this.styles[tn], function(v) {
23878                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
23879                     });
23880                 }
23881                 
23882                 st.store.loadData(avs);
23883                 st.collapse();
23884                 st.setValue(cn);
23885             }
23886             // flag our selected Node.
23887             this.tb.selectedNode = sel;
23888            
23889            
23890             Roo.menu.MenuMgr.hideAll();
23891
23892         }
23893         
23894         if (!updateFooter) {
23895             //this.footDisp.dom.innerHTML = ''; 
23896             return;
23897         }
23898         // update the footer
23899         //
23900         var html = '';
23901         
23902         this.footerEls = ans.reverse();
23903         Roo.each(this.footerEls, function(a,i) {
23904             if (!a) { return; }
23905             html += html.length ? ' &gt; '  :  '';
23906             
23907             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
23908             
23909         });
23910        
23911         // 
23912         var sz = this.footDisp.up('td').getSize();
23913         this.footDisp.dom.style.width = (sz.width -10) + 'px';
23914         this.footDisp.dom.style.marginLeft = '5px';
23915         
23916         this.footDisp.dom.style.overflow = 'hidden';
23917         
23918         this.footDisp.dom.innerHTML = html;
23919             
23920         //this.editorsyncValue();
23921     },
23922      
23923     
23924    
23925        
23926     // private
23927     onDestroy : function(){
23928         if(this.rendered){
23929             
23930             this.tb.items.each(function(item){
23931                 if(item.menu){
23932                     item.menu.removeAll();
23933                     if(item.menu.el){
23934                         item.menu.el.destroy();
23935                     }
23936                 }
23937                 item.destroy();
23938             });
23939              
23940         }
23941     },
23942     onFirstFocus: function() {
23943         // need to do this for all the toolbars..
23944         this.tb.items.each(function(item){
23945            item.enable();
23946         });
23947     },
23948     buildToolbar: function(tlist, nm)
23949     {
23950         var editor = this.editor;
23951         var editorcore = this.editorcore;
23952          // create a new element.
23953         var wdiv = editor.wrap.createChild({
23954                 tag: 'div'
23955             }, editor.wrap.dom.firstChild.nextSibling, true);
23956         
23957        
23958         var tb = new Roo.Toolbar(wdiv);
23959         // add the name..
23960         
23961         tb.add(nm+ ":&nbsp;");
23962         
23963         var styles = [];
23964         for(var i in this.styles) {
23965             styles.push(i);
23966         }
23967         
23968         // styles...
23969         if (styles && styles.length) {
23970             
23971             // this needs a multi-select checkbox...
23972             tb.addField( new Roo.form.ComboBox({
23973                 store: new Roo.data.SimpleStore({
23974                     id : 'val',
23975                     fields: ['val', 'selected'],
23976                     data : [] 
23977                 }),
23978                 name : '-roo-edit-className',
23979                 attrname : 'className',
23980                 displayField: 'val',
23981                 typeAhead: false,
23982                 mode: 'local',
23983                 editable : false,
23984                 triggerAction: 'all',
23985                 emptyText:'Select Style',
23986                 selectOnFocus:true,
23987                 width: 130,
23988                 listeners : {
23989                     'select': function(c, r, i) {
23990                         // initial support only for on class per el..
23991                         tb.selectedNode.className =  r ? r.get('val') : '';
23992                         editorcore.syncValue();
23993                     }
23994                 }
23995     
23996             }));
23997         }
23998         
23999         var tbc = Roo.form.HtmlEditor.ToolbarContext;
24000         var tbops = tbc.options;
24001         
24002         for (var i in tlist) {
24003             
24004             var item = tlist[i];
24005             tb.add(item.title + ":&nbsp;");
24006             
24007             
24008             //optname == used so you can configure the options available..
24009             var opts = item.opts ? item.opts : false;
24010             if (item.optname) {
24011                 opts = tbops[item.optname];
24012            
24013             }
24014             
24015             if (opts) {
24016                 // opts == pulldown..
24017                 tb.addField( new Roo.form.ComboBox({
24018                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24019                         id : 'val',
24020                         fields: ['val', 'display'],
24021                         data : opts  
24022                     }),
24023                     name : '-roo-edit-' + i,
24024                     attrname : i,
24025                     stylename : item.style ? item.style : false,
24026                     displayField: item.displayField ? item.displayField : 'val',
24027                     valueField :  'val',
24028                     typeAhead: false,
24029                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
24030                     editable : false,
24031                     triggerAction: 'all',
24032                     emptyText:'Select',
24033                     selectOnFocus:true,
24034                     width: item.width ? item.width  : 130,
24035                     listeners : {
24036                         'select': function(c, r, i) {
24037                             if (c.stylename) {
24038                                 tb.selectedNode.style[c.stylename] =  r.get('val');
24039                                 return;
24040                             }
24041                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24042                         }
24043                     }
24044
24045                 }));
24046                 continue;
24047                     
24048                  
24049                 
24050                 tb.addField( new Roo.form.TextField({
24051                     name: i,
24052                     width: 100,
24053                     //allowBlank:false,
24054                     value: ''
24055                 }));
24056                 continue;
24057             }
24058             tb.addField( new Roo.form.TextField({
24059                 name: '-roo-edit-' + i,
24060                 attrname : i,
24061                 
24062                 width: item.width,
24063                 //allowBlank:true,
24064                 value: '',
24065                 listeners: {
24066                     'change' : function(f, nv, ov) {
24067                         tb.selectedNode.setAttribute(f.attrname, nv);
24068                         editorcore.syncValue();
24069                     }
24070                 }
24071             }));
24072              
24073         }
24074         
24075         var _this = this;
24076         
24077         if(nm == 'BODY'){
24078             tb.addSeparator();
24079         
24080             tb.addButton( {
24081                 text: 'Stylesheets',
24082
24083                 listeners : {
24084                     click : function ()
24085                     {
24086                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
24087                     }
24088                 }
24089             });
24090         }
24091         
24092         tb.addFill();
24093         tb.addButton( {
24094             text: 'Remove Tag',
24095     
24096             listeners : {
24097                 click : function ()
24098                 {
24099                     // remove
24100                     // undo does not work.
24101                      
24102                     var sn = tb.selectedNode;
24103                     
24104                     var pn = sn.parentNode;
24105                     
24106                     var stn =  sn.childNodes[0];
24107                     var en = sn.childNodes[sn.childNodes.length - 1 ];
24108                     while (sn.childNodes.length) {
24109                         var node = sn.childNodes[0];
24110                         sn.removeChild(node);
24111                         //Roo.log(node);
24112                         pn.insertBefore(node, sn);
24113                         
24114                     }
24115                     pn.removeChild(sn);
24116                     var range = editorcore.createRange();
24117         
24118                     range.setStart(stn,0);
24119                     range.setEnd(en,0); //????
24120                     //range.selectNode(sel);
24121                     
24122                     
24123                     var selection = editorcore.getSelection();
24124                     selection.removeAllRanges();
24125                     selection.addRange(range);
24126                     
24127                     
24128                     
24129                     //_this.updateToolbar(null, null, pn);
24130                     _this.updateToolbar(null, null, null);
24131                     _this.footDisp.dom.innerHTML = ''; 
24132                 }
24133             }
24134             
24135                     
24136                 
24137             
24138         });
24139         
24140         
24141         tb.el.on('click', function(e){
24142             e.preventDefault(); // what does this do?
24143         });
24144         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24145         tb.el.hide();
24146         tb.name = nm;
24147         // dont need to disable them... as they will get hidden
24148         return tb;
24149          
24150         
24151     },
24152     buildFooter : function()
24153     {
24154         
24155         var fel = this.editor.wrap.createChild();
24156         this.footer = new Roo.Toolbar(fel);
24157         // toolbar has scrolly on left / right?
24158         var footDisp= new Roo.Toolbar.Fill();
24159         var _t = this;
24160         this.footer.add(
24161             {
24162                 text : '&lt;',
24163                 xtype: 'Button',
24164                 handler : function() {
24165                     _t.footDisp.scrollTo('left',0,true)
24166                 }
24167             }
24168         );
24169         this.footer.add( footDisp );
24170         this.footer.add( 
24171             {
24172                 text : '&gt;',
24173                 xtype: 'Button',
24174                 handler : function() {
24175                     // no animation..
24176                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24177                 }
24178             }
24179         );
24180         var fel = Roo.get(footDisp.el);
24181         fel.addClass('x-editor-context');
24182         this.footDispWrap = fel; 
24183         this.footDispWrap.overflow  = 'hidden';
24184         
24185         this.footDisp = fel.createChild();
24186         this.footDispWrap.on('click', this.onContextClick, this)
24187         
24188         
24189     },
24190     onContextClick : function (ev,dom)
24191     {
24192         ev.preventDefault();
24193         var  cn = dom.className;
24194         //Roo.log(cn);
24195         if (!cn.match(/x-ed-loc-/)) {
24196             return;
24197         }
24198         var n = cn.split('-').pop();
24199         var ans = this.footerEls;
24200         var sel = ans[n];
24201         
24202          // pick
24203         var range = this.editorcore.createRange();
24204         
24205         range.selectNodeContents(sel);
24206         //range.selectNode(sel);
24207         
24208         
24209         var selection = this.editorcore.getSelection();
24210         selection.removeAllRanges();
24211         selection.addRange(range);
24212         
24213         
24214         
24215         this.updateToolbar(null, null, sel);
24216         
24217         
24218     }
24219     
24220     
24221     
24222     
24223     
24224 });
24225
24226
24227
24228
24229
24230 /*
24231  * Based on:
24232  * Ext JS Library 1.1.1
24233  * Copyright(c) 2006-2007, Ext JS, LLC.
24234  *
24235  * Originally Released Under LGPL - original licence link has changed is not relivant.
24236  *
24237  * Fork - LGPL
24238  * <script type="text/javascript">
24239  */
24240  
24241 /**
24242  * @class Roo.form.BasicForm
24243  * @extends Roo.util.Observable
24244  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24245  * @constructor
24246  * @param {String/HTMLElement/Roo.Element} el The form element or its id
24247  * @param {Object} config Configuration options
24248  */
24249 Roo.form.BasicForm = function(el, config){
24250     this.allItems = [];
24251     this.childForms = [];
24252     Roo.apply(this, config);
24253     /*
24254      * The Roo.form.Field items in this form.
24255      * @type MixedCollection
24256      */
24257      
24258      
24259     this.items = new Roo.util.MixedCollection(false, function(o){
24260         return o.id || (o.id = Roo.id());
24261     });
24262     this.addEvents({
24263         /**
24264          * @event beforeaction
24265          * Fires before any action is performed. Return false to cancel the action.
24266          * @param {Form} this
24267          * @param {Action} action The action to be performed
24268          */
24269         beforeaction: true,
24270         /**
24271          * @event actionfailed
24272          * Fires when an action fails.
24273          * @param {Form} this
24274          * @param {Action} action The action that failed
24275          */
24276         actionfailed : true,
24277         /**
24278          * @event actioncomplete
24279          * Fires when an action is completed.
24280          * @param {Form} this
24281          * @param {Action} action The action that completed
24282          */
24283         actioncomplete : true
24284     });
24285     if(el){
24286         this.initEl(el);
24287     }
24288     Roo.form.BasicForm.superclass.constructor.call(this);
24289 };
24290
24291 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24292     /**
24293      * @cfg {String} method
24294      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24295      */
24296     /**
24297      * @cfg {DataReader} reader
24298      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24299      * This is optional as there is built-in support for processing JSON.
24300      */
24301     /**
24302      * @cfg {DataReader} errorReader
24303      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24304      * This is completely optional as there is built-in support for processing JSON.
24305      */
24306     /**
24307      * @cfg {String} url
24308      * The URL to use for form actions if one isn't supplied in the action options.
24309      */
24310     /**
24311      * @cfg {Boolean} fileUpload
24312      * Set to true if this form is a file upload.
24313      */
24314      
24315     /**
24316      * @cfg {Object} baseParams
24317      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24318      */
24319      /**
24320      
24321     /**
24322      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24323      */
24324     timeout: 30,
24325
24326     // private
24327     activeAction : null,
24328
24329     /**
24330      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24331      * or setValues() data instead of when the form was first created.
24332      */
24333     trackResetOnLoad : false,
24334     
24335     
24336     /**
24337      * childForms - used for multi-tab forms
24338      * @type {Array}
24339      */
24340     childForms : false,
24341     
24342     /**
24343      * allItems - full list of fields.
24344      * @type {Array}
24345      */
24346     allItems : false,
24347     
24348     /**
24349      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24350      * element by passing it or its id or mask the form itself by passing in true.
24351      * @type Mixed
24352      */
24353     waitMsgTarget : false,
24354
24355     // private
24356     initEl : function(el){
24357         this.el = Roo.get(el);
24358         this.id = this.el.id || Roo.id();
24359         this.el.on('submit', this.onSubmit, this);
24360         this.el.addClass('x-form');
24361     },
24362
24363     // private
24364     onSubmit : function(e){
24365         e.stopEvent();
24366     },
24367
24368     /**
24369      * Returns true if client-side validation on the form is successful.
24370      * @return Boolean
24371      */
24372     isValid : function(){
24373         var valid = true;
24374         this.items.each(function(f){
24375            if(!f.validate()){
24376                valid = false;
24377            }
24378         });
24379         return valid;
24380     },
24381
24382     /**
24383      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
24384      * @return Boolean
24385      */
24386     isDirty : function(){
24387         var dirty = false;
24388         this.items.each(function(f){
24389            if(f.isDirty()){
24390                dirty = true;
24391                return false;
24392            }
24393         });
24394         return dirty;
24395     },
24396     
24397     /**
24398      * Returns true if any fields in this form have changed since their original load. (New version)
24399      * @return Boolean
24400      */
24401     
24402     hasChanged : function()
24403     {
24404         var dirty = false;
24405         this.items.each(function(f){
24406            if(f.hasChanged()){
24407                dirty = true;
24408                return false;
24409            }
24410         });
24411         return dirty;
24412         
24413     },
24414     /**
24415      * Resets all hasChanged to 'false' -
24416      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24417      * So hasChanged storage is only to be used for this purpose
24418      * @return Boolean
24419      */
24420     resetHasChanged : function()
24421     {
24422         this.items.each(function(f){
24423            f.resetHasChanged();
24424         });
24425         
24426     },
24427     
24428     
24429     /**
24430      * Performs a predefined action (submit or load) or custom actions you define on this form.
24431      * @param {String} actionName The name of the action type
24432      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
24433      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24434      * accept other config options):
24435      * <pre>
24436 Property          Type             Description
24437 ----------------  ---------------  ----------------------------------------------------------------------------------
24438 url               String           The url for the action (defaults to the form's url)
24439 method            String           The form method to use (defaults to the form's method, or POST if not defined)
24440 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
24441 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
24442                                    validate the form on the client (defaults to false)
24443      * </pre>
24444      * @return {BasicForm} this
24445      */
24446     doAction : function(action, options){
24447         if(typeof action == 'string'){
24448             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
24449         }
24450         if(this.fireEvent('beforeaction', this, action) !== false){
24451             this.beforeAction(action);
24452             action.run.defer(100, action);
24453         }
24454         return this;
24455     },
24456
24457     /**
24458      * Shortcut to do a submit action.
24459      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24460      * @return {BasicForm} this
24461      */
24462     submit : function(options){
24463         this.doAction('submit', options);
24464         return this;
24465     },
24466
24467     /**
24468      * Shortcut to do a load action.
24469      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24470      * @return {BasicForm} this
24471      */
24472     load : function(options){
24473         this.doAction('load', options);
24474         return this;
24475     },
24476
24477     /**
24478      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
24479      * @param {Record} record The record to edit
24480      * @return {BasicForm} this
24481      */
24482     updateRecord : function(record){
24483         record.beginEdit();
24484         var fs = record.fields;
24485         fs.each(function(f){
24486             var field = this.findField(f.name);
24487             if(field){
24488                 record.set(f.name, field.getValue());
24489             }
24490         }, this);
24491         record.endEdit();
24492         return this;
24493     },
24494
24495     /**
24496      * Loads an Roo.data.Record into this form.
24497      * @param {Record} record The record to load
24498      * @return {BasicForm} this
24499      */
24500     loadRecord : function(record){
24501         this.setValues(record.data);
24502         return this;
24503     },
24504
24505     // private
24506     beforeAction : function(action){
24507         var o = action.options;
24508         
24509        
24510         if(this.waitMsgTarget === true){
24511             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
24512         }else if(this.waitMsgTarget){
24513             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
24514             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
24515         }else {
24516             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
24517         }
24518          
24519     },
24520
24521     // private
24522     afterAction : function(action, success){
24523         this.activeAction = null;
24524         var o = action.options;
24525         
24526         if(this.waitMsgTarget === true){
24527             this.el.unmask();
24528         }else if(this.waitMsgTarget){
24529             this.waitMsgTarget.unmask();
24530         }else{
24531             Roo.MessageBox.updateProgress(1);
24532             Roo.MessageBox.hide();
24533         }
24534          
24535         if(success){
24536             if(o.reset){
24537                 this.reset();
24538             }
24539             Roo.callback(o.success, o.scope, [this, action]);
24540             this.fireEvent('actioncomplete', this, action);
24541             
24542         }else{
24543             
24544             // failure condition..
24545             // we have a scenario where updates need confirming.
24546             // eg. if a locking scenario exists..
24547             // we look for { errors : { needs_confirm : true }} in the response.
24548             if (
24549                 (typeof(action.result) != 'undefined')  &&
24550                 (typeof(action.result.errors) != 'undefined')  &&
24551                 (typeof(action.result.errors.needs_confirm) != 'undefined')
24552            ){
24553                 var _t = this;
24554                 Roo.MessageBox.confirm(
24555                     "Change requires confirmation",
24556                     action.result.errorMsg,
24557                     function(r) {
24558                         if (r != 'yes') {
24559                             return;
24560                         }
24561                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
24562                     }
24563                     
24564                 );
24565                 
24566                 
24567                 
24568                 return;
24569             }
24570             
24571             Roo.callback(o.failure, o.scope, [this, action]);
24572             // show an error message if no failed handler is set..
24573             if (!this.hasListener('actionfailed')) {
24574                 Roo.MessageBox.alert("Error",
24575                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
24576                         action.result.errorMsg :
24577                         "Saving Failed, please check your entries or try again"
24578                 );
24579             }
24580             
24581             this.fireEvent('actionfailed', this, action);
24582         }
24583         
24584     },
24585
24586     /**
24587      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
24588      * @param {String} id The value to search for
24589      * @return Field
24590      */
24591     findField : function(id){
24592         var field = this.items.get(id);
24593         if(!field){
24594             this.items.each(function(f){
24595                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
24596                     field = f;
24597                     return false;
24598                 }
24599             });
24600         }
24601         return field || null;
24602     },
24603
24604     /**
24605      * Add a secondary form to this one, 
24606      * Used to provide tabbed forms. One form is primary, with hidden values 
24607      * which mirror the elements from the other forms.
24608      * 
24609      * @param {Roo.form.Form} form to add.
24610      * 
24611      */
24612     addForm : function(form)
24613     {
24614        
24615         if (this.childForms.indexOf(form) > -1) {
24616             // already added..
24617             return;
24618         }
24619         this.childForms.push(form);
24620         var n = '';
24621         Roo.each(form.allItems, function (fe) {
24622             
24623             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
24624             if (this.findField(n)) { // already added..
24625                 return;
24626             }
24627             var add = new Roo.form.Hidden({
24628                 name : n
24629             });
24630             add.render(this.el);
24631             
24632             this.add( add );
24633         }, this);
24634         
24635     },
24636     /**
24637      * Mark fields in this form invalid in bulk.
24638      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
24639      * @return {BasicForm} this
24640      */
24641     markInvalid : function(errors){
24642         if(errors instanceof Array){
24643             for(var i = 0, len = errors.length; i < len; i++){
24644                 var fieldError = errors[i];
24645                 var f = this.findField(fieldError.id);
24646                 if(f){
24647                     f.markInvalid(fieldError.msg);
24648                 }
24649             }
24650         }else{
24651             var field, id;
24652             for(id in errors){
24653                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
24654                     field.markInvalid(errors[id]);
24655                 }
24656             }
24657         }
24658         Roo.each(this.childForms || [], function (f) {
24659             f.markInvalid(errors);
24660         });
24661         
24662         return this;
24663     },
24664
24665     /**
24666      * Set values for fields in this form in bulk.
24667      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
24668      * @return {BasicForm} this
24669      */
24670     setValues : function(values){
24671         if(values instanceof Array){ // array of objects
24672             for(var i = 0, len = values.length; i < len; i++){
24673                 var v = values[i];
24674                 var f = this.findField(v.id);
24675                 if(f){
24676                     f.setValue(v.value);
24677                     if(this.trackResetOnLoad){
24678                         f.originalValue = f.getValue();
24679                     }
24680                 }
24681             }
24682         }else{ // object hash
24683             var field, id;
24684             for(id in values){
24685                 if(typeof values[id] != 'function' && (field = this.findField(id))){
24686                     
24687                     if (field.setFromData && 
24688                         field.valueField && 
24689                         field.displayField &&
24690                         // combos' with local stores can 
24691                         // be queried via setValue()
24692                         // to set their value..
24693                         (field.store && !field.store.isLocal)
24694                         ) {
24695                         // it's a combo
24696                         var sd = { };
24697                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
24698                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
24699                         field.setFromData(sd);
24700                         
24701                     } else {
24702                         field.setValue(values[id]);
24703                     }
24704                     
24705                     
24706                     if(this.trackResetOnLoad){
24707                         field.originalValue = field.getValue();
24708                     }
24709                 }
24710             }
24711         }
24712         this.resetHasChanged();
24713         
24714         
24715         Roo.each(this.childForms || [], function (f) {
24716             f.setValues(values);
24717             f.resetHasChanged();
24718         });
24719                 
24720         return this;
24721     },
24722
24723     /**
24724      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
24725      * they are returned as an array.
24726      * @param {Boolean} asString
24727      * @return {Object}
24728      */
24729     getValues : function(asString){
24730         if (this.childForms) {
24731             // copy values from the child forms
24732             Roo.each(this.childForms, function (f) {
24733                 this.setValues(f.getValues());
24734             }, this);
24735         }
24736         
24737         
24738         
24739         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
24740         if(asString === true){
24741             return fs;
24742         }
24743         return Roo.urlDecode(fs);
24744     },
24745     
24746     /**
24747      * Returns the fields in this form as an object with key/value pairs. 
24748      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
24749      * @return {Object}
24750      */
24751     getFieldValues : function(with_hidden)
24752     {
24753         if (this.childForms) {
24754             // copy values from the child forms
24755             // should this call getFieldValues - probably not as we do not currently copy
24756             // hidden fields when we generate..
24757             Roo.each(this.childForms, function (f) {
24758                 this.setValues(f.getValues());
24759             }, this);
24760         }
24761         
24762         var ret = {};
24763         this.items.each(function(f){
24764             if (!f.getName()) {
24765                 return;
24766             }
24767             var v = f.getValue();
24768             if (f.inputType =='radio') {
24769                 if (typeof(ret[f.getName()]) == 'undefined') {
24770                     ret[f.getName()] = ''; // empty..
24771                 }
24772                 
24773                 if (!f.el.dom.checked) {
24774                     return;
24775                     
24776                 }
24777                 v = f.el.dom.value;
24778                 
24779             }
24780             
24781             // not sure if this supported any more..
24782             if ((typeof(v) == 'object') && f.getRawValue) {
24783                 v = f.getRawValue() ; // dates..
24784             }
24785             // combo boxes where name != hiddenName...
24786             if (f.name != f.getName()) {
24787                 ret[f.name] = f.getRawValue();
24788             }
24789             ret[f.getName()] = v;
24790         });
24791         
24792         return ret;
24793     },
24794
24795     /**
24796      * Clears all invalid messages in this form.
24797      * @return {BasicForm} this
24798      */
24799     clearInvalid : function(){
24800         this.items.each(function(f){
24801            f.clearInvalid();
24802         });
24803         
24804         Roo.each(this.childForms || [], function (f) {
24805             f.clearInvalid();
24806         });
24807         
24808         
24809         return this;
24810     },
24811
24812     /**
24813      * Resets this form.
24814      * @return {BasicForm} this
24815      */
24816     reset : function(){
24817         this.items.each(function(f){
24818             f.reset();
24819         });
24820         
24821         Roo.each(this.childForms || [], function (f) {
24822             f.reset();
24823         });
24824         this.resetHasChanged();
24825         
24826         return this;
24827     },
24828
24829     /**
24830      * Add Roo.form components to this form.
24831      * @param {Field} field1
24832      * @param {Field} field2 (optional)
24833      * @param {Field} etc (optional)
24834      * @return {BasicForm} this
24835      */
24836     add : function(){
24837         this.items.addAll(Array.prototype.slice.call(arguments, 0));
24838         return this;
24839     },
24840
24841
24842     /**
24843      * Removes a field from the items collection (does NOT remove its markup).
24844      * @param {Field} field
24845      * @return {BasicForm} this
24846      */
24847     remove : function(field){
24848         this.items.remove(field);
24849         return this;
24850     },
24851
24852     /**
24853      * Looks at the fields in this form, checks them for an id attribute,
24854      * and calls applyTo on the existing dom element with that id.
24855      * @return {BasicForm} this
24856      */
24857     render : function(){
24858         this.items.each(function(f){
24859             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
24860                 f.applyTo(f.id);
24861             }
24862         });
24863         return this;
24864     },
24865
24866     /**
24867      * Calls {@link Ext#apply} for all fields in this form with the passed object.
24868      * @param {Object} values
24869      * @return {BasicForm} this
24870      */
24871     applyToFields : function(o){
24872         this.items.each(function(f){
24873            Roo.apply(f, o);
24874         });
24875         return this;
24876     },
24877
24878     /**
24879      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
24880      * @param {Object} values
24881      * @return {BasicForm} this
24882      */
24883     applyIfToFields : function(o){
24884         this.items.each(function(f){
24885            Roo.applyIf(f, o);
24886         });
24887         return this;
24888     }
24889 });
24890
24891 // back compat
24892 Roo.BasicForm = Roo.form.BasicForm;/*
24893  * Based on:
24894  * Ext JS Library 1.1.1
24895  * Copyright(c) 2006-2007, Ext JS, LLC.
24896  *
24897  * Originally Released Under LGPL - original licence link has changed is not relivant.
24898  *
24899  * Fork - LGPL
24900  * <script type="text/javascript">
24901  */
24902
24903 /**
24904  * @class Roo.form.Form
24905  * @extends Roo.form.BasicForm
24906  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
24907  * @constructor
24908  * @param {Object} config Configuration options
24909  */
24910 Roo.form.Form = function(config){
24911     var xitems =  [];
24912     if (config.items) {
24913         xitems = config.items;
24914         delete config.items;
24915     }
24916    
24917     
24918     Roo.form.Form.superclass.constructor.call(this, null, config);
24919     this.url = this.url || this.action;
24920     if(!this.root){
24921         this.root = new Roo.form.Layout(Roo.applyIf({
24922             id: Roo.id()
24923         }, config));
24924     }
24925     this.active = this.root;
24926     /**
24927      * Array of all the buttons that have been added to this form via {@link addButton}
24928      * @type Array
24929      */
24930     this.buttons = [];
24931     this.allItems = [];
24932     this.addEvents({
24933         /**
24934          * @event clientvalidation
24935          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
24936          * @param {Form} this
24937          * @param {Boolean} valid true if the form has passed client-side validation
24938          */
24939         clientvalidation: true,
24940         /**
24941          * @event rendered
24942          * Fires when the form is rendered
24943          * @param {Roo.form.Form} form
24944          */
24945         rendered : true
24946     });
24947     
24948     if (this.progressUrl) {
24949             // push a hidden field onto the list of fields..
24950             this.addxtype( {
24951                     xns: Roo.form, 
24952                     xtype : 'Hidden', 
24953                     name : 'UPLOAD_IDENTIFIER' 
24954             });
24955         }
24956         
24957     
24958     Roo.each(xitems, this.addxtype, this);
24959     
24960     
24961     
24962 };
24963
24964 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
24965     /**
24966      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
24967      */
24968     /**
24969      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
24970      */
24971     /**
24972      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
24973      */
24974     buttonAlign:'center',
24975
24976     /**
24977      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
24978      */
24979     minButtonWidth:75,
24980
24981     /**
24982      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
24983      * This property cascades to child containers if not set.
24984      */
24985     labelAlign:'left',
24986
24987     /**
24988      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
24989      * fires a looping event with that state. This is required to bind buttons to the valid
24990      * state using the config value formBind:true on the button.
24991      */
24992     monitorValid : false,
24993
24994     /**
24995      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
24996      */
24997     monitorPoll : 200,
24998     
24999     /**
25000      * @cfg {String} progressUrl - Url to return progress data 
25001      */
25002     
25003     progressUrl : false,
25004     /**
25005      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25006      * sending a formdata with extra parameters - eg uploaded elements.
25007      */
25008     
25009     formData : false,
25010     
25011     /**
25012      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25013      * fields are added and the column is closed. If no fields are passed the column remains open
25014      * until end() is called.
25015      * @param {Object} config The config to pass to the column
25016      * @param {Field} field1 (optional)
25017      * @param {Field} field2 (optional)
25018      * @param {Field} etc (optional)
25019      * @return Column The column container object
25020      */
25021     column : function(c){
25022         var col = new Roo.form.Column(c);
25023         this.start(col);
25024         if(arguments.length > 1){ // duplicate code required because of Opera
25025             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25026             this.end();
25027         }
25028         return col;
25029     },
25030
25031     /**
25032      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25033      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25034      * until end() is called.
25035      * @param {Object} config The config to pass to the fieldset
25036      * @param {Field} field1 (optional)
25037      * @param {Field} field2 (optional)
25038      * @param {Field} etc (optional)
25039      * @return FieldSet The fieldset container object
25040      */
25041     fieldset : function(c){
25042         var fs = new Roo.form.FieldSet(c);
25043         this.start(fs);
25044         if(arguments.length > 1){ // duplicate code required because of Opera
25045             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25046             this.end();
25047         }
25048         return fs;
25049     },
25050
25051     /**
25052      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25053      * fields are added and the container is closed. If no fields are passed the container remains open
25054      * until end() is called.
25055      * @param {Object} config The config to pass to the Layout
25056      * @param {Field} field1 (optional)
25057      * @param {Field} field2 (optional)
25058      * @param {Field} etc (optional)
25059      * @return Layout The container object
25060      */
25061     container : function(c){
25062         var l = new Roo.form.Layout(c);
25063         this.start(l);
25064         if(arguments.length > 1){ // duplicate code required because of Opera
25065             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25066             this.end();
25067         }
25068         return l;
25069     },
25070
25071     /**
25072      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25073      * @param {Object} container A Roo.form.Layout or subclass of Layout
25074      * @return {Form} this
25075      */
25076     start : function(c){
25077         // cascade label info
25078         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25079         this.active.stack.push(c);
25080         c.ownerCt = this.active;
25081         this.active = c;
25082         return this;
25083     },
25084
25085     /**
25086      * Closes the current open container
25087      * @return {Form} this
25088      */
25089     end : function(){
25090         if(this.active == this.root){
25091             return this;
25092         }
25093         this.active = this.active.ownerCt;
25094         return this;
25095     },
25096
25097     /**
25098      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25099      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25100      * as the label of the field.
25101      * @param {Field} field1
25102      * @param {Field} field2 (optional)
25103      * @param {Field} etc. (optional)
25104      * @return {Form} this
25105      */
25106     add : function(){
25107         this.active.stack.push.apply(this.active.stack, arguments);
25108         this.allItems.push.apply(this.allItems,arguments);
25109         var r = [];
25110         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25111             if(a[i].isFormField){
25112                 r.push(a[i]);
25113             }
25114         }
25115         if(r.length > 0){
25116             Roo.form.Form.superclass.add.apply(this, r);
25117         }
25118         return this;
25119     },
25120     
25121
25122     
25123     
25124     
25125      /**
25126      * Find any element that has been added to a form, using it's ID or name
25127      * This can include framesets, columns etc. along with regular fields..
25128      * @param {String} id - id or name to find.
25129      
25130      * @return {Element} e - or false if nothing found.
25131      */
25132     findbyId : function(id)
25133     {
25134         var ret = false;
25135         if (!id) {
25136             return ret;
25137         }
25138         Roo.each(this.allItems, function(f){
25139             if (f.id == id || f.name == id ){
25140                 ret = f;
25141                 return false;
25142             }
25143         });
25144         return ret;
25145     },
25146
25147     
25148     
25149     /**
25150      * Render this form into the passed container. This should only be called once!
25151      * @param {String/HTMLElement/Element} container The element this component should be rendered into
25152      * @return {Form} this
25153      */
25154     render : function(ct)
25155     {
25156         
25157         
25158         
25159         ct = Roo.get(ct);
25160         var o = this.autoCreate || {
25161             tag: 'form',
25162             method : this.method || 'POST',
25163             id : this.id || Roo.id()
25164         };
25165         this.initEl(ct.createChild(o));
25166
25167         this.root.render(this.el);
25168         
25169        
25170              
25171         this.items.each(function(f){
25172             f.render('x-form-el-'+f.id);
25173         });
25174
25175         if(this.buttons.length > 0){
25176             // tables are required to maintain order and for correct IE layout
25177             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25178                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25179                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25180             }}, null, true);
25181             var tr = tb.getElementsByTagName('tr')[0];
25182             for(var i = 0, len = this.buttons.length; i < len; i++) {
25183                 var b = this.buttons[i];
25184                 var td = document.createElement('td');
25185                 td.className = 'x-form-btn-td';
25186                 b.render(tr.appendChild(td));
25187             }
25188         }
25189         if(this.monitorValid){ // initialize after render
25190             this.startMonitoring();
25191         }
25192         this.fireEvent('rendered', this);
25193         return this;
25194     },
25195
25196     /**
25197      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25198      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25199      * object or a valid Roo.DomHelper element config
25200      * @param {Function} handler The function called when the button is clicked
25201      * @param {Object} scope (optional) The scope of the handler function
25202      * @return {Roo.Button}
25203      */
25204     addButton : function(config, handler, scope){
25205         var bc = {
25206             handler: handler,
25207             scope: scope,
25208             minWidth: this.minButtonWidth,
25209             hideParent:true
25210         };
25211         if(typeof config == "string"){
25212             bc.text = config;
25213         }else{
25214             Roo.apply(bc, config);
25215         }
25216         var btn = new Roo.Button(null, bc);
25217         this.buttons.push(btn);
25218         return btn;
25219     },
25220
25221      /**
25222      * Adds a series of form elements (using the xtype property as the factory method.
25223      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25224      * @param {Object} config 
25225      */
25226     
25227     addxtype : function()
25228     {
25229         var ar = Array.prototype.slice.call(arguments, 0);
25230         var ret = false;
25231         for(var i = 0; i < ar.length; i++) {
25232             if (!ar[i]) {
25233                 continue; // skip -- if this happends something invalid got sent, we 
25234                 // should ignore it, as basically that interface element will not show up
25235                 // and that should be pretty obvious!!
25236             }
25237             
25238             if (Roo.form[ar[i].xtype]) {
25239                 ar[i].form = this;
25240                 var fe = Roo.factory(ar[i], Roo.form);
25241                 if (!ret) {
25242                     ret = fe;
25243                 }
25244                 fe.form = this;
25245                 if (fe.store) {
25246                     fe.store.form = this;
25247                 }
25248                 if (fe.isLayout) {  
25249                          
25250                     this.start(fe);
25251                     this.allItems.push(fe);
25252                     if (fe.items && fe.addxtype) {
25253                         fe.addxtype.apply(fe, fe.items);
25254                         delete fe.items;
25255                     }
25256                      this.end();
25257                     continue;
25258                 }
25259                 
25260                 
25261                  
25262                 this.add(fe);
25263               //  console.log('adding ' + ar[i].xtype);
25264             }
25265             if (ar[i].xtype == 'Button') {  
25266                 //console.log('adding button');
25267                 //console.log(ar[i]);
25268                 this.addButton(ar[i]);
25269                 this.allItems.push(fe);
25270                 continue;
25271             }
25272             
25273             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25274                 alert('end is not supported on xtype any more, use items');
25275             //    this.end();
25276             //    //console.log('adding end');
25277             }
25278             
25279         }
25280         return ret;
25281     },
25282     
25283     /**
25284      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
25285      * option "monitorValid"
25286      */
25287     startMonitoring : function(){
25288         if(!this.bound){
25289             this.bound = true;
25290             Roo.TaskMgr.start({
25291                 run : this.bindHandler,
25292                 interval : this.monitorPoll || 200,
25293                 scope: this
25294             });
25295         }
25296     },
25297
25298     /**
25299      * Stops monitoring of the valid state of this form
25300      */
25301     stopMonitoring : function(){
25302         this.bound = false;
25303     },
25304
25305     // private
25306     bindHandler : function(){
25307         if(!this.bound){
25308             return false; // stops binding
25309         }
25310         var valid = true;
25311         this.items.each(function(f){
25312             if(!f.isValid(true)){
25313                 valid = false;
25314                 return false;
25315             }
25316         });
25317         for(var i = 0, len = this.buttons.length; i < len; i++){
25318             var btn = this.buttons[i];
25319             if(btn.formBind === true && btn.disabled === valid){
25320                 btn.setDisabled(!valid);
25321             }
25322         }
25323         this.fireEvent('clientvalidation', this, valid);
25324     }
25325     
25326     
25327     
25328     
25329     
25330     
25331     
25332     
25333 });
25334
25335
25336 // back compat
25337 Roo.Form = Roo.form.Form;
25338 /*
25339  * Based on:
25340  * Ext JS Library 1.1.1
25341  * Copyright(c) 2006-2007, Ext JS, LLC.
25342  *
25343  * Originally Released Under LGPL - original licence link has changed is not relivant.
25344  *
25345  * Fork - LGPL
25346  * <script type="text/javascript">
25347  */
25348
25349 // as we use this in bootstrap.
25350 Roo.namespace('Roo.form');
25351  /**
25352  * @class Roo.form.Action
25353  * Internal Class used to handle form actions
25354  * @constructor
25355  * @param {Roo.form.BasicForm} el The form element or its id
25356  * @param {Object} config Configuration options
25357  */
25358
25359  
25360  
25361 // define the action interface
25362 Roo.form.Action = function(form, options){
25363     this.form = form;
25364     this.options = options || {};
25365 };
25366 /**
25367  * Client Validation Failed
25368  * @const 
25369  */
25370 Roo.form.Action.CLIENT_INVALID = 'client';
25371 /**
25372  * Server Validation Failed
25373  * @const 
25374  */
25375 Roo.form.Action.SERVER_INVALID = 'server';
25376  /**
25377  * Connect to Server Failed
25378  * @const 
25379  */
25380 Roo.form.Action.CONNECT_FAILURE = 'connect';
25381 /**
25382  * Reading Data from Server Failed
25383  * @const 
25384  */
25385 Roo.form.Action.LOAD_FAILURE = 'load';
25386
25387 Roo.form.Action.prototype = {
25388     type : 'default',
25389     failureType : undefined,
25390     response : undefined,
25391     result : undefined,
25392
25393     // interface method
25394     run : function(options){
25395
25396     },
25397
25398     // interface method
25399     success : function(response){
25400
25401     },
25402
25403     // interface method
25404     handleResponse : function(response){
25405
25406     },
25407
25408     // default connection failure
25409     failure : function(response){
25410         
25411         this.response = response;
25412         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25413         this.form.afterAction(this, false);
25414     },
25415
25416     processResponse : function(response){
25417         this.response = response;
25418         if(!response.responseText){
25419             return true;
25420         }
25421         this.result = this.handleResponse(response);
25422         return this.result;
25423     },
25424
25425     // utility functions used internally
25426     getUrl : function(appendParams){
25427         var url = this.options.url || this.form.url || this.form.el.dom.action;
25428         if(appendParams){
25429             var p = this.getParams();
25430             if(p){
25431                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
25432             }
25433         }
25434         return url;
25435     },
25436
25437     getMethod : function(){
25438         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
25439     },
25440
25441     getParams : function(){
25442         var bp = this.form.baseParams;
25443         var p = this.options.params;
25444         if(p){
25445             if(typeof p == "object"){
25446                 p = Roo.urlEncode(Roo.applyIf(p, bp));
25447             }else if(typeof p == 'string' && bp){
25448                 p += '&' + Roo.urlEncode(bp);
25449             }
25450         }else if(bp){
25451             p = Roo.urlEncode(bp);
25452         }
25453         return p;
25454     },
25455
25456     createCallback : function(){
25457         return {
25458             success: this.success,
25459             failure: this.failure,
25460             scope: this,
25461             timeout: (this.form.timeout*1000),
25462             upload: this.form.fileUpload ? this.success : undefined
25463         };
25464     }
25465 };
25466
25467 Roo.form.Action.Submit = function(form, options){
25468     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
25469 };
25470
25471 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
25472     type : 'submit',
25473
25474     haveProgress : false,
25475     uploadComplete : false,
25476     
25477     // uploadProgress indicator.
25478     uploadProgress : function()
25479     {
25480         if (!this.form.progressUrl) {
25481             return;
25482         }
25483         
25484         if (!this.haveProgress) {
25485             Roo.MessageBox.progress("Uploading", "Uploading");
25486         }
25487         if (this.uploadComplete) {
25488            Roo.MessageBox.hide();
25489            return;
25490         }
25491         
25492         this.haveProgress = true;
25493    
25494         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
25495         
25496         var c = new Roo.data.Connection();
25497         c.request({
25498             url : this.form.progressUrl,
25499             params: {
25500                 id : uid
25501             },
25502             method: 'GET',
25503             success : function(req){
25504                //console.log(data);
25505                 var rdata = false;
25506                 var edata;
25507                 try  {
25508                    rdata = Roo.decode(req.responseText)
25509                 } catch (e) {
25510                     Roo.log("Invalid data from server..");
25511                     Roo.log(edata);
25512                     return;
25513                 }
25514                 if (!rdata || !rdata.success) {
25515                     Roo.log(rdata);
25516                     Roo.MessageBox.alert(Roo.encode(rdata));
25517                     return;
25518                 }
25519                 var data = rdata.data;
25520                 
25521                 if (this.uploadComplete) {
25522                    Roo.MessageBox.hide();
25523                    return;
25524                 }
25525                    
25526                 if (data){
25527                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
25528                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
25529                     );
25530                 }
25531                 this.uploadProgress.defer(2000,this);
25532             },
25533        
25534             failure: function(data) {
25535                 Roo.log('progress url failed ');
25536                 Roo.log(data);
25537             },
25538             scope : this
25539         });
25540            
25541     },
25542     
25543     
25544     run : function()
25545     {
25546         // run get Values on the form, so it syncs any secondary forms.
25547         this.form.getValues();
25548         
25549         var o = this.options;
25550         var method = this.getMethod();
25551         var isPost = method == 'POST';
25552         if(o.clientValidation === false || this.form.isValid()){
25553             
25554             if (this.form.progressUrl) {
25555                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
25556                     (new Date() * 1) + '' + Math.random());
25557                     
25558             } 
25559             
25560             
25561             Roo.Ajax.request(Roo.apply(this.createCallback(), {
25562                 form:this.form.el.dom,
25563                 url:this.getUrl(!isPost),
25564                 method: method,
25565                 params:isPost ? this.getParams() : null,
25566                 isUpload: this.form.fileUpload,
25567                 formData : this.form.formData
25568             }));
25569             
25570             this.uploadProgress();
25571
25572         }else if (o.clientValidation !== false){ // client validation failed
25573             this.failureType = Roo.form.Action.CLIENT_INVALID;
25574             this.form.afterAction(this, false);
25575         }
25576     },
25577
25578     success : function(response)
25579     {
25580         this.uploadComplete= true;
25581         if (this.haveProgress) {
25582             Roo.MessageBox.hide();
25583         }
25584         
25585         
25586         var result = this.processResponse(response);
25587         if(result === true || result.success){
25588             this.form.afterAction(this, true);
25589             return;
25590         }
25591         if(result.errors){
25592             this.form.markInvalid(result.errors);
25593             this.failureType = Roo.form.Action.SERVER_INVALID;
25594         }
25595         this.form.afterAction(this, false);
25596     },
25597     failure : function(response)
25598     {
25599         this.uploadComplete= true;
25600         if (this.haveProgress) {
25601             Roo.MessageBox.hide();
25602         }
25603         
25604         this.response = response;
25605         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25606         this.form.afterAction(this, false);
25607     },
25608     
25609     handleResponse : function(response){
25610         if(this.form.errorReader){
25611             var rs = this.form.errorReader.read(response);
25612             var errors = [];
25613             if(rs.records){
25614                 for(var i = 0, len = rs.records.length; i < len; i++) {
25615                     var r = rs.records[i];
25616                     errors[i] = r.data;
25617                 }
25618             }
25619             if(errors.length < 1){
25620                 errors = null;
25621             }
25622             return {
25623                 success : rs.success,
25624                 errors : errors
25625             };
25626         }
25627         var ret = false;
25628         try {
25629             ret = Roo.decode(response.responseText);
25630         } catch (e) {
25631             ret = {
25632                 success: false,
25633                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
25634                 errors : []
25635             };
25636         }
25637         return ret;
25638         
25639     }
25640 });
25641
25642
25643 Roo.form.Action.Load = function(form, options){
25644     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
25645     this.reader = this.form.reader;
25646 };
25647
25648 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
25649     type : 'load',
25650
25651     run : function(){
25652         
25653         Roo.Ajax.request(Roo.apply(
25654                 this.createCallback(), {
25655                     method:this.getMethod(),
25656                     url:this.getUrl(false),
25657                     params:this.getParams()
25658         }));
25659     },
25660
25661     success : function(response){
25662         
25663         var result = this.processResponse(response);
25664         if(result === true || !result.success || !result.data){
25665             this.failureType = Roo.form.Action.LOAD_FAILURE;
25666             this.form.afterAction(this, false);
25667             return;
25668         }
25669         this.form.clearInvalid();
25670         this.form.setValues(result.data);
25671         this.form.afterAction(this, true);
25672     },
25673
25674     handleResponse : function(response){
25675         if(this.form.reader){
25676             var rs = this.form.reader.read(response);
25677             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
25678             return {
25679                 success : rs.success,
25680                 data : data
25681             };
25682         }
25683         return Roo.decode(response.responseText);
25684     }
25685 });
25686
25687 Roo.form.Action.ACTION_TYPES = {
25688     'load' : Roo.form.Action.Load,
25689     'submit' : Roo.form.Action.Submit
25690 };/*
25691  * Based on:
25692  * Ext JS Library 1.1.1
25693  * Copyright(c) 2006-2007, Ext JS, LLC.
25694  *
25695  * Originally Released Under LGPL - original licence link has changed is not relivant.
25696  *
25697  * Fork - LGPL
25698  * <script type="text/javascript">
25699  */
25700  
25701 /**
25702  * @class Roo.form.Layout
25703  * @extends Roo.Component
25704  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
25705  * @constructor
25706  * @param {Object} config Configuration options
25707  */
25708 Roo.form.Layout = function(config){
25709     var xitems = [];
25710     if (config.items) {
25711         xitems = config.items;
25712         delete config.items;
25713     }
25714     Roo.form.Layout.superclass.constructor.call(this, config);
25715     this.stack = [];
25716     Roo.each(xitems, this.addxtype, this);
25717      
25718 };
25719
25720 Roo.extend(Roo.form.Layout, Roo.Component, {
25721     /**
25722      * @cfg {String/Object} autoCreate
25723      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
25724      */
25725     /**
25726      * @cfg {String/Object/Function} style
25727      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
25728      * a function which returns such a specification.
25729      */
25730     /**
25731      * @cfg {String} labelAlign
25732      * Valid values are "left," "top" and "right" (defaults to "left")
25733      */
25734     /**
25735      * @cfg {Number} labelWidth
25736      * Fixed width in pixels of all field labels (defaults to undefined)
25737      */
25738     /**
25739      * @cfg {Boolean} clear
25740      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
25741      */
25742     clear : true,
25743     /**
25744      * @cfg {String} labelSeparator
25745      * The separator to use after field labels (defaults to ':')
25746      */
25747     labelSeparator : ':',
25748     /**
25749      * @cfg {Boolean} hideLabels
25750      * True to suppress the display of field labels in this layout (defaults to false)
25751      */
25752     hideLabels : false,
25753
25754     // private
25755     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
25756     
25757     isLayout : true,
25758     
25759     // private
25760     onRender : function(ct, position){
25761         if(this.el){ // from markup
25762             this.el = Roo.get(this.el);
25763         }else {  // generate
25764             var cfg = this.getAutoCreate();
25765             this.el = ct.createChild(cfg, position);
25766         }
25767         if(this.style){
25768             this.el.applyStyles(this.style);
25769         }
25770         if(this.labelAlign){
25771             this.el.addClass('x-form-label-'+this.labelAlign);
25772         }
25773         if(this.hideLabels){
25774             this.labelStyle = "display:none";
25775             this.elementStyle = "padding-left:0;";
25776         }else{
25777             if(typeof this.labelWidth == 'number'){
25778                 this.labelStyle = "width:"+this.labelWidth+"px;";
25779                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
25780             }
25781             if(this.labelAlign == 'top'){
25782                 this.labelStyle = "width:auto;";
25783                 this.elementStyle = "padding-left:0;";
25784             }
25785         }
25786         var stack = this.stack;
25787         var slen = stack.length;
25788         if(slen > 0){
25789             if(!this.fieldTpl){
25790                 var t = new Roo.Template(
25791                     '<div class="x-form-item {5}">',
25792                         '<label for="{0}" style="{2}">{1}{4}</label>',
25793                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
25794                         '</div>',
25795                     '</div><div class="x-form-clear-left"></div>'
25796                 );
25797                 t.disableFormats = true;
25798                 t.compile();
25799                 Roo.form.Layout.prototype.fieldTpl = t;
25800             }
25801             for(var i = 0; i < slen; i++) {
25802                 if(stack[i].isFormField){
25803                     this.renderField(stack[i]);
25804                 }else{
25805                     this.renderComponent(stack[i]);
25806                 }
25807             }
25808         }
25809         if(this.clear){
25810             this.el.createChild({cls:'x-form-clear'});
25811         }
25812     },
25813
25814     // private
25815     renderField : function(f){
25816         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
25817                f.id, //0
25818                f.fieldLabel, //1
25819                f.labelStyle||this.labelStyle||'', //2
25820                this.elementStyle||'', //3
25821                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
25822                f.itemCls||this.itemCls||''  //5
25823        ], true).getPrevSibling());
25824     },
25825
25826     // private
25827     renderComponent : function(c){
25828         c.render(c.isLayout ? this.el : this.el.createChild());    
25829     },
25830     /**
25831      * Adds a object form elements (using the xtype property as the factory method.)
25832      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
25833      * @param {Object} config 
25834      */
25835     addxtype : function(o)
25836     {
25837         // create the lement.
25838         o.form = this.form;
25839         var fe = Roo.factory(o, Roo.form);
25840         this.form.allItems.push(fe);
25841         this.stack.push(fe);
25842         
25843         if (fe.isFormField) {
25844             this.form.items.add(fe);
25845         }
25846          
25847         return fe;
25848     }
25849 });
25850
25851 /**
25852  * @class Roo.form.Column
25853  * @extends Roo.form.Layout
25854  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
25855  * @constructor
25856  * @param {Object} config Configuration options
25857  */
25858 Roo.form.Column = function(config){
25859     Roo.form.Column.superclass.constructor.call(this, config);
25860 };
25861
25862 Roo.extend(Roo.form.Column, Roo.form.Layout, {
25863     /**
25864      * @cfg {Number/String} width
25865      * The fixed width of the column in pixels or CSS value (defaults to "auto")
25866      */
25867     /**
25868      * @cfg {String/Object} autoCreate
25869      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
25870      */
25871
25872     // private
25873     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
25874
25875     // private
25876     onRender : function(ct, position){
25877         Roo.form.Column.superclass.onRender.call(this, ct, position);
25878         if(this.width){
25879             this.el.setWidth(this.width);
25880         }
25881     }
25882 });
25883
25884
25885 /**
25886  * @class Roo.form.Row
25887  * @extends Roo.form.Layout
25888  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
25889  * @constructor
25890  * @param {Object} config Configuration options
25891  */
25892
25893  
25894 Roo.form.Row = function(config){
25895     Roo.form.Row.superclass.constructor.call(this, config);
25896 };
25897  
25898 Roo.extend(Roo.form.Row, Roo.form.Layout, {
25899       /**
25900      * @cfg {Number/String} width
25901      * The fixed width of the column in pixels or CSS value (defaults to "auto")
25902      */
25903     /**
25904      * @cfg {Number/String} height
25905      * The fixed height of the column in pixels or CSS value (defaults to "auto")
25906      */
25907     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
25908     
25909     padWidth : 20,
25910     // private
25911     onRender : function(ct, position){
25912         //console.log('row render');
25913         if(!this.rowTpl){
25914             var t = new Roo.Template(
25915                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
25916                     '<label for="{0}" style="{2}">{1}{4}</label>',
25917                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
25918                     '</div>',
25919                 '</div>'
25920             );
25921             t.disableFormats = true;
25922             t.compile();
25923             Roo.form.Layout.prototype.rowTpl = t;
25924         }
25925         this.fieldTpl = this.rowTpl;
25926         
25927         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
25928         var labelWidth = 100;
25929         
25930         if ((this.labelAlign != 'top')) {
25931             if (typeof this.labelWidth == 'number') {
25932                 labelWidth = this.labelWidth
25933             }
25934             this.padWidth =  20 + labelWidth;
25935             
25936         }
25937         
25938         Roo.form.Column.superclass.onRender.call(this, ct, position);
25939         if(this.width){
25940             this.el.setWidth(this.width);
25941         }
25942         if(this.height){
25943             this.el.setHeight(this.height);
25944         }
25945     },
25946     
25947     // private
25948     renderField : function(f){
25949         f.fieldEl = this.fieldTpl.append(this.el, [
25950                f.id, f.fieldLabel,
25951                f.labelStyle||this.labelStyle||'',
25952                this.elementStyle||'',
25953                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
25954                f.itemCls||this.itemCls||'',
25955                f.width ? f.width + this.padWidth : 160 + this.padWidth
25956        ],true);
25957     }
25958 });
25959  
25960
25961 /**
25962  * @class Roo.form.FieldSet
25963  * @extends Roo.form.Layout
25964  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
25965  * @constructor
25966  * @param {Object} config Configuration options
25967  */
25968 Roo.form.FieldSet = function(config){
25969     Roo.form.FieldSet.superclass.constructor.call(this, config);
25970 };
25971
25972 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
25973     /**
25974      * @cfg {String} legend
25975      * The text to display as the legend for the FieldSet (defaults to '')
25976      */
25977     /**
25978      * @cfg {String/Object} autoCreate
25979      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
25980      */
25981
25982     // private
25983     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
25984
25985     // private
25986     onRender : function(ct, position){
25987         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
25988         if(this.legend){
25989             this.setLegend(this.legend);
25990         }
25991     },
25992
25993     // private
25994     setLegend : function(text){
25995         if(this.rendered){
25996             this.el.child('legend').update(text);
25997         }
25998     }
25999 });/*
26000  * Based on:
26001  * Ext JS Library 1.1.1
26002  * Copyright(c) 2006-2007, Ext JS, LLC.
26003  *
26004  * Originally Released Under LGPL - original licence link has changed is not relivant.
26005  *
26006  * Fork - LGPL
26007  * <script type="text/javascript">
26008  */
26009 /**
26010  * @class Roo.form.VTypes
26011  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26012  * @singleton
26013  */
26014 Roo.form.VTypes = function(){
26015     // closure these in so they are only created once.
26016     var alpha = /^[a-zA-Z_]+$/;
26017     var alphanum = /^[a-zA-Z0-9_]+$/;
26018     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26019     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26020
26021     // All these messages and functions are configurable
26022     return {
26023         /**
26024          * The function used to validate email addresses
26025          * @param {String} value The email address
26026          */
26027         'email' : function(v){
26028             return email.test(v);
26029         },
26030         /**
26031          * The error text to display when the email validation function returns false
26032          * @type String
26033          */
26034         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26035         /**
26036          * The keystroke filter mask to be applied on email input
26037          * @type RegExp
26038          */
26039         'emailMask' : /[a-z0-9_\.\-@]/i,
26040
26041         /**
26042          * The function used to validate URLs
26043          * @param {String} value The URL
26044          */
26045         'url' : function(v){
26046             return url.test(v);
26047         },
26048         /**
26049          * The error text to display when the url validation function returns false
26050          * @type String
26051          */
26052         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26053         
26054         /**
26055          * The function used to validate alpha values
26056          * @param {String} value The value
26057          */
26058         'alpha' : function(v){
26059             return alpha.test(v);
26060         },
26061         /**
26062          * The error text to display when the alpha validation function returns false
26063          * @type String
26064          */
26065         'alphaText' : 'This field should only contain letters and _',
26066         /**
26067          * The keystroke filter mask to be applied on alpha input
26068          * @type RegExp
26069          */
26070         'alphaMask' : /[a-z_]/i,
26071
26072         /**
26073          * The function used to validate alphanumeric values
26074          * @param {String} value The value
26075          */
26076         'alphanum' : function(v){
26077             return alphanum.test(v);
26078         },
26079         /**
26080          * The error text to display when the alphanumeric validation function returns false
26081          * @type String
26082          */
26083         'alphanumText' : 'This field should only contain letters, numbers and _',
26084         /**
26085          * The keystroke filter mask to be applied on alphanumeric input
26086          * @type RegExp
26087          */
26088         'alphanumMask' : /[a-z0-9_]/i
26089     };
26090 }();//<script type="text/javascript">
26091
26092 /**
26093  * @class Roo.form.FCKeditor
26094  * @extends Roo.form.TextArea
26095  * Wrapper around the FCKEditor http://www.fckeditor.net
26096  * @constructor
26097  * Creates a new FCKeditor
26098  * @param {Object} config Configuration options
26099  */
26100 Roo.form.FCKeditor = function(config){
26101     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26102     this.addEvents({
26103          /**
26104          * @event editorinit
26105          * Fired when the editor is initialized - you can add extra handlers here..
26106          * @param {FCKeditor} this
26107          * @param {Object} the FCK object.
26108          */
26109         editorinit : true
26110     });
26111     
26112     
26113 };
26114 Roo.form.FCKeditor.editors = { };
26115 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26116 {
26117     //defaultAutoCreate : {
26118     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26119     //},
26120     // private
26121     /**
26122      * @cfg {Object} fck options - see fck manual for details.
26123      */
26124     fckconfig : false,
26125     
26126     /**
26127      * @cfg {Object} fck toolbar set (Basic or Default)
26128      */
26129     toolbarSet : 'Basic',
26130     /**
26131      * @cfg {Object} fck BasePath
26132      */ 
26133     basePath : '/fckeditor/',
26134     
26135     
26136     frame : false,
26137     
26138     value : '',
26139     
26140    
26141     onRender : function(ct, position)
26142     {
26143         if(!this.el){
26144             this.defaultAutoCreate = {
26145                 tag: "textarea",
26146                 style:"width:300px;height:60px;",
26147                 autocomplete: "new-password"
26148             };
26149         }
26150         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26151         /*
26152         if(this.grow){
26153             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26154             if(this.preventScrollbars){
26155                 this.el.setStyle("overflow", "hidden");
26156             }
26157             this.el.setHeight(this.growMin);
26158         }
26159         */
26160         //console.log('onrender' + this.getId() );
26161         Roo.form.FCKeditor.editors[this.getId()] = this;
26162          
26163
26164         this.replaceTextarea() ;
26165         
26166     },
26167     
26168     getEditor : function() {
26169         return this.fckEditor;
26170     },
26171     /**
26172      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26173      * @param {Mixed} value The value to set
26174      */
26175     
26176     
26177     setValue : function(value)
26178     {
26179         //console.log('setValue: ' + value);
26180         
26181         if(typeof(value) == 'undefined') { // not sure why this is happending...
26182             return;
26183         }
26184         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26185         
26186         //if(!this.el || !this.getEditor()) {
26187         //    this.value = value;
26188             //this.setValue.defer(100,this,[value]);    
26189         //    return;
26190         //} 
26191         
26192         if(!this.getEditor()) {
26193             return;
26194         }
26195         
26196         this.getEditor().SetData(value);
26197         
26198         //
26199
26200     },
26201
26202     /**
26203      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26204      * @return {Mixed} value The field value
26205      */
26206     getValue : function()
26207     {
26208         
26209         if (this.frame && this.frame.dom.style.display == 'none') {
26210             return Roo.form.FCKeditor.superclass.getValue.call(this);
26211         }
26212         
26213         if(!this.el || !this.getEditor()) {
26214            
26215            // this.getValue.defer(100,this); 
26216             return this.value;
26217         }
26218        
26219         
26220         var value=this.getEditor().GetData();
26221         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26222         return Roo.form.FCKeditor.superclass.getValue.call(this);
26223         
26224
26225     },
26226
26227     /**
26228      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26229      * @return {Mixed} value The field value
26230      */
26231     getRawValue : function()
26232     {
26233         if (this.frame && this.frame.dom.style.display == 'none') {
26234             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26235         }
26236         
26237         if(!this.el || !this.getEditor()) {
26238             //this.getRawValue.defer(100,this); 
26239             return this.value;
26240             return;
26241         }
26242         
26243         
26244         
26245         var value=this.getEditor().GetData();
26246         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26247         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26248          
26249     },
26250     
26251     setSize : function(w,h) {
26252         
26253         
26254         
26255         //if (this.frame && this.frame.dom.style.display == 'none') {
26256         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26257         //    return;
26258         //}
26259         //if(!this.el || !this.getEditor()) {
26260         //    this.setSize.defer(100,this, [w,h]); 
26261         //    return;
26262         //}
26263         
26264         
26265         
26266         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26267         
26268         this.frame.dom.setAttribute('width', w);
26269         this.frame.dom.setAttribute('height', h);
26270         this.frame.setSize(w,h);
26271         
26272     },
26273     
26274     toggleSourceEdit : function(value) {
26275         
26276       
26277          
26278         this.el.dom.style.display = value ? '' : 'none';
26279         this.frame.dom.style.display = value ?  'none' : '';
26280         
26281     },
26282     
26283     
26284     focus: function(tag)
26285     {
26286         if (this.frame.dom.style.display == 'none') {
26287             return Roo.form.FCKeditor.superclass.focus.call(this);
26288         }
26289         if(!this.el || !this.getEditor()) {
26290             this.focus.defer(100,this, [tag]); 
26291             return;
26292         }
26293         
26294         
26295         
26296         
26297         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
26298         this.getEditor().Focus();
26299         if (tgs.length) {
26300             if (!this.getEditor().Selection.GetSelection()) {
26301                 this.focus.defer(100,this, [tag]); 
26302                 return;
26303             }
26304             
26305             
26306             var r = this.getEditor().EditorDocument.createRange();
26307             r.setStart(tgs[0],0);
26308             r.setEnd(tgs[0],0);
26309             this.getEditor().Selection.GetSelection().removeAllRanges();
26310             this.getEditor().Selection.GetSelection().addRange(r);
26311             this.getEditor().Focus();
26312         }
26313         
26314     },
26315     
26316     
26317     
26318     replaceTextarea : function()
26319     {
26320         if ( document.getElementById( this.getId() + '___Frame' ) ) {
26321             return ;
26322         }
26323         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
26324         //{
26325             // We must check the elements firstly using the Id and then the name.
26326         var oTextarea = document.getElementById( this.getId() );
26327         
26328         var colElementsByName = document.getElementsByName( this.getId() ) ;
26329          
26330         oTextarea.style.display = 'none' ;
26331
26332         if ( oTextarea.tabIndex ) {            
26333             this.TabIndex = oTextarea.tabIndex ;
26334         }
26335         
26336         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
26337         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
26338         this.frame = Roo.get(this.getId() + '___Frame')
26339     },
26340     
26341     _getConfigHtml : function()
26342     {
26343         var sConfig = '' ;
26344
26345         for ( var o in this.fckconfig ) {
26346             sConfig += sConfig.length > 0  ? '&amp;' : '';
26347             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
26348         }
26349
26350         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
26351     },
26352     
26353     
26354     _getIFrameHtml : function()
26355     {
26356         var sFile = 'fckeditor.html' ;
26357         /* no idea what this is about..
26358         try
26359         {
26360             if ( (/fcksource=true/i).test( window.top.location.search ) )
26361                 sFile = 'fckeditor.original.html' ;
26362         }
26363         catch (e) { 
26364         */
26365
26366         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
26367         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
26368         
26369         
26370         var html = '<iframe id="' + this.getId() +
26371             '___Frame" src="' + sLink +
26372             '" width="' + this.width +
26373             '" height="' + this.height + '"' +
26374             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
26375             ' frameborder="0" scrolling="no"></iframe>' ;
26376
26377         return html ;
26378     },
26379     
26380     _insertHtmlBefore : function( html, element )
26381     {
26382         if ( element.insertAdjacentHTML )       {
26383             // IE
26384             element.insertAdjacentHTML( 'beforeBegin', html ) ;
26385         } else { // Gecko
26386             var oRange = document.createRange() ;
26387             oRange.setStartBefore( element ) ;
26388             var oFragment = oRange.createContextualFragment( html );
26389             element.parentNode.insertBefore( oFragment, element ) ;
26390         }
26391     }
26392     
26393     
26394   
26395     
26396     
26397     
26398     
26399
26400 });
26401
26402 //Roo.reg('fckeditor', Roo.form.FCKeditor);
26403
26404 function FCKeditor_OnComplete(editorInstance){
26405     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
26406     f.fckEditor = editorInstance;
26407     //console.log("loaded");
26408     f.fireEvent('editorinit', f, editorInstance);
26409
26410   
26411
26412  
26413
26414
26415
26416
26417
26418
26419
26420
26421
26422
26423
26424
26425
26426
26427
26428 //<script type="text/javascript">
26429 /**
26430  * @class Roo.form.GridField
26431  * @extends Roo.form.Field
26432  * Embed a grid (or editable grid into a form)
26433  * STATUS ALPHA
26434  * 
26435  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
26436  * it needs 
26437  * xgrid.store = Roo.data.Store
26438  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
26439  * xgrid.store.reader = Roo.data.JsonReader 
26440  * 
26441  * 
26442  * @constructor
26443  * Creates a new GridField
26444  * @param {Object} config Configuration options
26445  */
26446 Roo.form.GridField = function(config){
26447     Roo.form.GridField.superclass.constructor.call(this, config);
26448      
26449 };
26450
26451 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
26452     /**
26453      * @cfg {Number} width  - used to restrict width of grid..
26454      */
26455     width : 100,
26456     /**
26457      * @cfg {Number} height - used to restrict height of grid..
26458      */
26459     height : 50,
26460      /**
26461      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
26462          * 
26463          *}
26464      */
26465     xgrid : false, 
26466     /**
26467      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26468      * {tag: "input", type: "checkbox", autocomplete: "off"})
26469      */
26470    // defaultAutoCreate : { tag: 'div' },
26471     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
26472     /**
26473      * @cfg {String} addTitle Text to include for adding a title.
26474      */
26475     addTitle : false,
26476     //
26477     onResize : function(){
26478         Roo.form.Field.superclass.onResize.apply(this, arguments);
26479     },
26480
26481     initEvents : function(){
26482         // Roo.form.Checkbox.superclass.initEvents.call(this);
26483         // has no events...
26484        
26485     },
26486
26487
26488     getResizeEl : function(){
26489         return this.wrap;
26490     },
26491
26492     getPositionEl : function(){
26493         return this.wrap;
26494     },
26495
26496     // private
26497     onRender : function(ct, position){
26498         
26499         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
26500         var style = this.style;
26501         delete this.style;
26502         
26503         Roo.form.GridField.superclass.onRender.call(this, ct, position);
26504         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
26505         this.viewEl = this.wrap.createChild({ tag: 'div' });
26506         if (style) {
26507             this.viewEl.applyStyles(style);
26508         }
26509         if (this.width) {
26510             this.viewEl.setWidth(this.width);
26511         }
26512         if (this.height) {
26513             this.viewEl.setHeight(this.height);
26514         }
26515         //if(this.inputValue !== undefined){
26516         //this.setValue(this.value);
26517         
26518         
26519         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
26520         
26521         
26522         this.grid.render();
26523         this.grid.getDataSource().on('remove', this.refreshValue, this);
26524         this.grid.getDataSource().on('update', this.refreshValue, this);
26525         this.grid.on('afteredit', this.refreshValue, this);
26526  
26527     },
26528      
26529     
26530     /**
26531      * Sets the value of the item. 
26532      * @param {String} either an object  or a string..
26533      */
26534     setValue : function(v){
26535         //this.value = v;
26536         v = v || []; // empty set..
26537         // this does not seem smart - it really only affects memoryproxy grids..
26538         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
26539             var ds = this.grid.getDataSource();
26540             // assumes a json reader..
26541             var data = {}
26542             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
26543             ds.loadData( data);
26544         }
26545         // clear selection so it does not get stale.
26546         if (this.grid.sm) { 
26547             this.grid.sm.clearSelections();
26548         }
26549         
26550         Roo.form.GridField.superclass.setValue.call(this, v);
26551         this.refreshValue();
26552         // should load data in the grid really....
26553     },
26554     
26555     // private
26556     refreshValue: function() {
26557          var val = [];
26558         this.grid.getDataSource().each(function(r) {
26559             val.push(r.data);
26560         });
26561         this.el.dom.value = Roo.encode(val);
26562     }
26563     
26564      
26565     
26566     
26567 });/*
26568  * Based on:
26569  * Ext JS Library 1.1.1
26570  * Copyright(c) 2006-2007, Ext JS, LLC.
26571  *
26572  * Originally Released Under LGPL - original licence link has changed is not relivant.
26573  *
26574  * Fork - LGPL
26575  * <script type="text/javascript">
26576  */
26577 /**
26578  * @class Roo.form.DisplayField
26579  * @extends Roo.form.Field
26580  * A generic Field to display non-editable data.
26581  * @cfg {Boolean} closable (true|false) default false
26582  * @constructor
26583  * Creates a new Display Field item.
26584  * @param {Object} config Configuration options
26585  */
26586 Roo.form.DisplayField = function(config){
26587     Roo.form.DisplayField.superclass.constructor.call(this, config);
26588     
26589     this.addEvents({
26590         /**
26591          * @event close
26592          * Fires after the click the close btn
26593              * @param {Roo.form.DisplayField} this
26594              */
26595         close : true
26596     });
26597 };
26598
26599 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
26600     inputType:      'hidden',
26601     allowBlank:     true,
26602     readOnly:         true,
26603     
26604  
26605     /**
26606      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
26607      */
26608     focusClass : undefined,
26609     /**
26610      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
26611      */
26612     fieldClass: 'x-form-field',
26613     
26614      /**
26615      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
26616      */
26617     valueRenderer: undefined,
26618     
26619     width: 100,
26620     /**
26621      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26622      * {tag: "input", type: "checkbox", autocomplete: "off"})
26623      */
26624      
26625  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
26626  
26627     closable : false,
26628     
26629     onResize : function(){
26630         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
26631         
26632     },
26633
26634     initEvents : function(){
26635         // Roo.form.Checkbox.superclass.initEvents.call(this);
26636         // has no events...
26637         
26638         if(this.closable){
26639             this.closeEl.on('click', this.onClose, this);
26640         }
26641        
26642     },
26643
26644
26645     getResizeEl : function(){
26646         return this.wrap;
26647     },
26648
26649     getPositionEl : function(){
26650         return this.wrap;
26651     },
26652
26653     // private
26654     onRender : function(ct, position){
26655         
26656         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
26657         //if(this.inputValue !== undefined){
26658         this.wrap = this.el.wrap();
26659         
26660         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
26661         
26662         if(this.closable){
26663             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
26664         }
26665         
26666         if (this.bodyStyle) {
26667             this.viewEl.applyStyles(this.bodyStyle);
26668         }
26669         //this.viewEl.setStyle('padding', '2px');
26670         
26671         this.setValue(this.value);
26672         
26673     },
26674 /*
26675     // private
26676     initValue : Roo.emptyFn,
26677
26678   */
26679
26680         // private
26681     onClick : function(){
26682         
26683     },
26684
26685     /**
26686      * Sets the checked state of the checkbox.
26687      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
26688      */
26689     setValue : function(v){
26690         this.value = v;
26691         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
26692         // this might be called before we have a dom element..
26693         if (!this.viewEl) {
26694             return;
26695         }
26696         this.viewEl.dom.innerHTML = html;
26697         Roo.form.DisplayField.superclass.setValue.call(this, v);
26698
26699     },
26700     
26701     onClose : function(e)
26702     {
26703         e.preventDefault();
26704         
26705         this.fireEvent('close', this);
26706     }
26707 });/*
26708  * 
26709  * Licence- LGPL
26710  * 
26711  */
26712
26713 /**
26714  * @class Roo.form.DayPicker
26715  * @extends Roo.form.Field
26716  * A Day picker show [M] [T] [W] ....
26717  * @constructor
26718  * Creates a new Day Picker
26719  * @param {Object} config Configuration options
26720  */
26721 Roo.form.DayPicker= function(config){
26722     Roo.form.DayPicker.superclass.constructor.call(this, config);
26723      
26724 };
26725
26726 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
26727     /**
26728      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
26729      */
26730     focusClass : undefined,
26731     /**
26732      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
26733      */
26734     fieldClass: "x-form-field",
26735    
26736     /**
26737      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26738      * {tag: "input", type: "checkbox", autocomplete: "off"})
26739      */
26740     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
26741     
26742    
26743     actionMode : 'viewEl', 
26744     //
26745     // private
26746  
26747     inputType : 'hidden',
26748     
26749      
26750     inputElement: false, // real input element?
26751     basedOn: false, // ????
26752     
26753     isFormField: true, // not sure where this is needed!!!!
26754
26755     onResize : function(){
26756         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
26757         if(!this.boxLabel){
26758             this.el.alignTo(this.wrap, 'c-c');
26759         }
26760     },
26761
26762     initEvents : function(){
26763         Roo.form.Checkbox.superclass.initEvents.call(this);
26764         this.el.on("click", this.onClick,  this);
26765         this.el.on("change", this.onClick,  this);
26766     },
26767
26768
26769     getResizeEl : function(){
26770         return this.wrap;
26771     },
26772
26773     getPositionEl : function(){
26774         return this.wrap;
26775     },
26776
26777     
26778     // private
26779     onRender : function(ct, position){
26780         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
26781        
26782         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
26783         
26784         var r1 = '<table><tr>';
26785         var r2 = '<tr class="x-form-daypick-icons">';
26786         for (var i=0; i < 7; i++) {
26787             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
26788             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
26789         }
26790         
26791         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
26792         viewEl.select('img').on('click', this.onClick, this);
26793         this.viewEl = viewEl;   
26794         
26795         
26796         // this will not work on Chrome!!!
26797         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
26798         this.el.on('propertychange', this.setFromHidden,  this);  //ie
26799         
26800         
26801           
26802
26803     },
26804
26805     // private
26806     initValue : Roo.emptyFn,
26807
26808     /**
26809      * Returns the checked state of the checkbox.
26810      * @return {Boolean} True if checked, else false
26811      */
26812     getValue : function(){
26813         return this.el.dom.value;
26814         
26815     },
26816
26817         // private
26818     onClick : function(e){ 
26819         //this.setChecked(!this.checked);
26820         Roo.get(e.target).toggleClass('x-menu-item-checked');
26821         this.refreshValue();
26822         //if(this.el.dom.checked != this.checked){
26823         //    this.setValue(this.el.dom.checked);
26824        // }
26825     },
26826     
26827     // private
26828     refreshValue : function()
26829     {
26830         var val = '';
26831         this.viewEl.select('img',true).each(function(e,i,n)  {
26832             val += e.is(".x-menu-item-checked") ? String(n) : '';
26833         });
26834         this.setValue(val, true);
26835     },
26836
26837     /**
26838      * Sets the checked state of the checkbox.
26839      * On is always based on a string comparison between inputValue and the param.
26840      * @param {Boolean/String} value - the value to set 
26841      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
26842      */
26843     setValue : function(v,suppressEvent){
26844         if (!this.el.dom) {
26845             return;
26846         }
26847         var old = this.el.dom.value ;
26848         this.el.dom.value = v;
26849         if (suppressEvent) {
26850             return ;
26851         }
26852          
26853         // update display..
26854         this.viewEl.select('img',true).each(function(e,i,n)  {
26855             
26856             var on = e.is(".x-menu-item-checked");
26857             var newv = v.indexOf(String(n)) > -1;
26858             if (on != newv) {
26859                 e.toggleClass('x-menu-item-checked');
26860             }
26861             
26862         });
26863         
26864         
26865         this.fireEvent('change', this, v, old);
26866         
26867         
26868     },
26869    
26870     // handle setting of hidden value by some other method!!?!?
26871     setFromHidden: function()
26872     {
26873         if(!this.el){
26874             return;
26875         }
26876         //console.log("SET FROM HIDDEN");
26877         //alert('setFrom hidden');
26878         this.setValue(this.el.dom.value);
26879     },
26880     
26881     onDestroy : function()
26882     {
26883         if(this.viewEl){
26884             Roo.get(this.viewEl).remove();
26885         }
26886          
26887         Roo.form.DayPicker.superclass.onDestroy.call(this);
26888     }
26889
26890 });/*
26891  * RooJS Library 1.1.1
26892  * Copyright(c) 2008-2011  Alan Knowles
26893  *
26894  * License - LGPL
26895  */
26896  
26897
26898 /**
26899  * @class Roo.form.ComboCheck
26900  * @extends Roo.form.ComboBox
26901  * A combobox for multiple select items.
26902  *
26903  * FIXME - could do with a reset button..
26904  * 
26905  * @constructor
26906  * Create a new ComboCheck
26907  * @param {Object} config Configuration options
26908  */
26909 Roo.form.ComboCheck = function(config){
26910     Roo.form.ComboCheck.superclass.constructor.call(this, config);
26911     // should verify some data...
26912     // like
26913     // hiddenName = required..
26914     // displayField = required
26915     // valudField == required
26916     var req= [ 'hiddenName', 'displayField', 'valueField' ];
26917     var _t = this;
26918     Roo.each(req, function(e) {
26919         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
26920             throw "Roo.form.ComboCheck : missing value for: " + e;
26921         }
26922     });
26923     
26924     
26925 };
26926
26927 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
26928      
26929      
26930     editable : false,
26931      
26932     selectedClass: 'x-menu-item-checked', 
26933     
26934     // private
26935     onRender : function(ct, position){
26936         var _t = this;
26937         
26938         
26939         
26940         if(!this.tpl){
26941             var cls = 'x-combo-list';
26942
26943             
26944             this.tpl =  new Roo.Template({
26945                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
26946                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
26947                    '<span>{' + this.displayField + '}</span>' +
26948                     '</div>' 
26949                 
26950             });
26951         }
26952  
26953         
26954         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
26955         this.view.singleSelect = false;
26956         this.view.multiSelect = true;
26957         this.view.toggleSelect = true;
26958         this.pageTb.add(new Roo.Toolbar.Fill(), {
26959             
26960             text: 'Done',
26961             handler: function()
26962             {
26963                 _t.collapse();
26964             }
26965         });
26966     },
26967     
26968     onViewOver : function(e, t){
26969         // do nothing...
26970         return;
26971         
26972     },
26973     
26974     onViewClick : function(doFocus,index){
26975         return;
26976         
26977     },
26978     select: function () {
26979         //Roo.log("SELECT CALLED");
26980     },
26981      
26982     selectByValue : function(xv, scrollIntoView){
26983         var ar = this.getValueArray();
26984         var sels = [];
26985         
26986         Roo.each(ar, function(v) {
26987             if(v === undefined || v === null){
26988                 return;
26989             }
26990             var r = this.findRecord(this.valueField, v);
26991             if(r){
26992                 sels.push(this.store.indexOf(r))
26993                 
26994             }
26995         },this);
26996         this.view.select(sels);
26997         return false;
26998     },
26999     
27000     
27001     
27002     onSelect : function(record, index){
27003        // Roo.log("onselect Called");
27004        // this is only called by the clear button now..
27005         this.view.clearSelections();
27006         this.setValue('[]');
27007         if (this.value != this.valueBefore) {
27008             this.fireEvent('change', this, this.value, this.valueBefore);
27009             this.valueBefore = this.value;
27010         }
27011     },
27012     getValueArray : function()
27013     {
27014         var ar = [] ;
27015         
27016         try {
27017             //Roo.log(this.value);
27018             if (typeof(this.value) == 'undefined') {
27019                 return [];
27020             }
27021             var ar = Roo.decode(this.value);
27022             return  ar instanceof Array ? ar : []; //?? valid?
27023             
27024         } catch(e) {
27025             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
27026             return [];
27027         }
27028          
27029     },
27030     expand : function ()
27031     {
27032         
27033         Roo.form.ComboCheck.superclass.expand.call(this);
27034         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27035         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27036         
27037
27038     },
27039     
27040     collapse : function(){
27041         Roo.form.ComboCheck.superclass.collapse.call(this);
27042         var sl = this.view.getSelectedIndexes();
27043         var st = this.store;
27044         var nv = [];
27045         var tv = [];
27046         var r;
27047         Roo.each(sl, function(i) {
27048             r = st.getAt(i);
27049             nv.push(r.get(this.valueField));
27050         },this);
27051         this.setValue(Roo.encode(nv));
27052         if (this.value != this.valueBefore) {
27053
27054             this.fireEvent('change', this, this.value, this.valueBefore);
27055             this.valueBefore = this.value;
27056         }
27057         
27058     },
27059     
27060     setValue : function(v){
27061         // Roo.log(v);
27062         this.value = v;
27063         
27064         var vals = this.getValueArray();
27065         var tv = [];
27066         Roo.each(vals, function(k) {
27067             var r = this.findRecord(this.valueField, k);
27068             if(r){
27069                 tv.push(r.data[this.displayField]);
27070             }else if(this.valueNotFoundText !== undefined){
27071                 tv.push( this.valueNotFoundText );
27072             }
27073         },this);
27074        // Roo.log(tv);
27075         
27076         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27077         this.hiddenField.value = v;
27078         this.value = v;
27079     }
27080     
27081 });/*
27082  * Based on:
27083  * Ext JS Library 1.1.1
27084  * Copyright(c) 2006-2007, Ext JS, LLC.
27085  *
27086  * Originally Released Under LGPL - original licence link has changed is not relivant.
27087  *
27088  * Fork - LGPL
27089  * <script type="text/javascript">
27090  */
27091  
27092 /**
27093  * @class Roo.form.Signature
27094  * @extends Roo.form.Field
27095  * Signature field.  
27096  * @constructor
27097  * 
27098  * @param {Object} config Configuration options
27099  */
27100
27101 Roo.form.Signature = function(config){
27102     Roo.form.Signature.superclass.constructor.call(this, config);
27103     
27104     this.addEvents({// not in used??
27105          /**
27106          * @event confirm
27107          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27108              * @param {Roo.form.Signature} combo This combo box
27109              */
27110         'confirm' : true,
27111         /**
27112          * @event reset
27113          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27114              * @param {Roo.form.ComboBox} combo This combo box
27115              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27116              */
27117         'reset' : true
27118     });
27119 };
27120
27121 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
27122     /**
27123      * @cfg {Object} labels Label to use when rendering a form.
27124      * defaults to 
27125      * labels : { 
27126      *      clear : "Clear",
27127      *      confirm : "Confirm"
27128      *  }
27129      */
27130     labels : { 
27131         clear : "Clear",
27132         confirm : "Confirm"
27133     },
27134     /**
27135      * @cfg {Number} width The signature panel width (defaults to 300)
27136      */
27137     width: 300,
27138     /**
27139      * @cfg {Number} height The signature panel height (defaults to 100)
27140      */
27141     height : 100,
27142     /**
27143      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27144      */
27145     allowBlank : false,
27146     
27147     //private
27148     // {Object} signPanel The signature SVG panel element (defaults to {})
27149     signPanel : {},
27150     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27151     isMouseDown : false,
27152     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27153     isConfirmed : false,
27154     // {String} signatureTmp SVG mapping string (defaults to empty string)
27155     signatureTmp : '',
27156     
27157     
27158     defaultAutoCreate : { // modified by initCompnoent..
27159         tag: "input",
27160         type:"hidden"
27161     },
27162
27163     // private
27164     onRender : function(ct, position){
27165         
27166         Roo.form.Signature.superclass.onRender.call(this, ct, position);
27167         
27168         this.wrap = this.el.wrap({
27169             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27170         });
27171         
27172         this.createToolbar(this);
27173         this.signPanel = this.wrap.createChild({
27174                 tag: 'div',
27175                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27176             }, this.el
27177         );
27178             
27179         this.svgID = Roo.id();
27180         this.svgEl = this.signPanel.createChild({
27181               xmlns : 'http://www.w3.org/2000/svg',
27182               tag : 'svg',
27183               id : this.svgID + "-svg",
27184               width: this.width,
27185               height: this.height,
27186               viewBox: '0 0 '+this.width+' '+this.height,
27187               cn : [
27188                 {
27189                     tag: "rect",
27190                     id: this.svgID + "-svg-r",
27191                     width: this.width,
27192                     height: this.height,
27193                     fill: "#ffa"
27194                 },
27195                 {
27196                     tag: "line",
27197                     id: this.svgID + "-svg-l",
27198                     x1: "0", // start
27199                     y1: (this.height*0.8), // start set the line in 80% of height
27200                     x2: this.width, // end
27201                     y2: (this.height*0.8), // end set the line in 80% of height
27202                     'stroke': "#666",
27203                     'stroke-width': "1",
27204                     'stroke-dasharray': "3",
27205                     'shape-rendering': "crispEdges",
27206                     'pointer-events': "none"
27207                 },
27208                 {
27209                     tag: "path",
27210                     id: this.svgID + "-svg-p",
27211                     'stroke': "navy",
27212                     'stroke-width': "3",
27213                     'fill': "none",
27214                     'pointer-events': 'none'
27215                 }
27216               ]
27217         });
27218         this.createSVG();
27219         this.svgBox = this.svgEl.dom.getScreenCTM();
27220     },
27221     createSVG : function(){ 
27222         var svg = this.signPanel;
27223         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27224         var t = this;
27225
27226         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27227         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27228         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27229         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27230         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27231         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27232         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27233         
27234     },
27235     isTouchEvent : function(e){
27236         return e.type.match(/^touch/);
27237     },
27238     getCoords : function (e) {
27239         var pt    = this.svgEl.dom.createSVGPoint();
27240         pt.x = e.clientX; 
27241         pt.y = e.clientY;
27242         if (this.isTouchEvent(e)) {
27243             pt.x =  e.targetTouches[0].clientX;
27244             pt.y = e.targetTouches[0].clientY;
27245         }
27246         var a = this.svgEl.dom.getScreenCTM();
27247         var b = a.inverse();
27248         var mx = pt.matrixTransform(b);
27249         return mx.x + ',' + mx.y;
27250     },
27251     //mouse event headler 
27252     down : function (e) {
27253         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27254         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27255         
27256         this.isMouseDown = true;
27257         
27258         e.preventDefault();
27259     },
27260     move : function (e) {
27261         if (this.isMouseDown) {
27262             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27263             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27264         }
27265         
27266         e.preventDefault();
27267     },
27268     up : function (e) {
27269         this.isMouseDown = false;
27270         var sp = this.signatureTmp.split(' ');
27271         
27272         if(sp.length > 1){
27273             if(!sp[sp.length-2].match(/^L/)){
27274                 sp.pop();
27275                 sp.pop();
27276                 sp.push("");
27277                 this.signatureTmp = sp.join(" ");
27278             }
27279         }
27280         if(this.getValue() != this.signatureTmp){
27281             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27282             this.isConfirmed = false;
27283         }
27284         e.preventDefault();
27285     },
27286     
27287     /**
27288      * Protected method that will not generally be called directly. It
27289      * is called when the editor creates its toolbar. Override this method if you need to
27290      * add custom toolbar buttons.
27291      * @param {HtmlEditor} editor
27292      */
27293     createToolbar : function(editor){
27294          function btn(id, toggle, handler){
27295             var xid = fid + '-'+ id ;
27296             return {
27297                 id : xid,
27298                 cmd : id,
27299                 cls : 'x-btn-icon x-edit-'+id,
27300                 enableToggle:toggle !== false,
27301                 scope: editor, // was editor...
27302                 handler:handler||editor.relayBtnCmd,
27303                 clickEvent:'mousedown',
27304                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27305                 tabIndex:-1
27306             };
27307         }
27308         
27309         
27310         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27311         this.tb = tb;
27312         this.tb.add(
27313            {
27314                 cls : ' x-signature-btn x-signature-'+id,
27315                 scope: editor, // was editor...
27316                 handler: this.reset,
27317                 clickEvent:'mousedown',
27318                 text: this.labels.clear
27319             },
27320             {
27321                  xtype : 'Fill',
27322                  xns: Roo.Toolbar
27323             }, 
27324             {
27325                 cls : '  x-signature-btn x-signature-'+id,
27326                 scope: editor, // was editor...
27327                 handler: this.confirmHandler,
27328                 clickEvent:'mousedown',
27329                 text: this.labels.confirm
27330             }
27331         );
27332     
27333     },
27334     //public
27335     /**
27336      * when user is clicked confirm then show this image.....
27337      * 
27338      * @return {String} Image Data URI
27339      */
27340     getImageDataURI : function(){
27341         var svg = this.svgEl.dom.parentNode.innerHTML;
27342         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
27343         return src; 
27344     },
27345     /**
27346      * 
27347      * @return {Boolean} this.isConfirmed
27348      */
27349     getConfirmed : function(){
27350         return this.isConfirmed;
27351     },
27352     /**
27353      * 
27354      * @return {Number} this.width
27355      */
27356     getWidth : function(){
27357         return this.width;
27358     },
27359     /**
27360      * 
27361      * @return {Number} this.height
27362      */
27363     getHeight : function(){
27364         return this.height;
27365     },
27366     // private
27367     getSignature : function(){
27368         return this.signatureTmp;
27369     },
27370     // private
27371     reset : function(){
27372         this.signatureTmp = '';
27373         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27374         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
27375         this.isConfirmed = false;
27376         Roo.form.Signature.superclass.reset.call(this);
27377     },
27378     setSignature : function(s){
27379         this.signatureTmp = s;
27380         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27381         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
27382         this.setValue(s);
27383         this.isConfirmed = false;
27384         Roo.form.Signature.superclass.reset.call(this);
27385     }, 
27386     test : function(){
27387 //        Roo.log(this.signPanel.dom.contentWindow.up())
27388     },
27389     //private
27390     setConfirmed : function(){
27391         
27392         
27393         
27394 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
27395     },
27396     // private
27397     confirmHandler : function(){
27398         if(!this.getSignature()){
27399             return;
27400         }
27401         
27402         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
27403         this.setValue(this.getSignature());
27404         this.isConfirmed = true;
27405         
27406         this.fireEvent('confirm', this);
27407     },
27408     // private
27409     // Subclasses should provide the validation implementation by overriding this
27410     validateValue : function(value){
27411         if(this.allowBlank){
27412             return true;
27413         }
27414         
27415         if(this.isConfirmed){
27416             return true;
27417         }
27418         return false;
27419     }
27420 });/*
27421  * Based on:
27422  * Ext JS Library 1.1.1
27423  * Copyright(c) 2006-2007, Ext JS, LLC.
27424  *
27425  * Originally Released Under LGPL - original licence link has changed is not relivant.
27426  *
27427  * Fork - LGPL
27428  * <script type="text/javascript">
27429  */
27430  
27431
27432 /**
27433  * @class Roo.form.ComboBox
27434  * @extends Roo.form.TriggerField
27435  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
27436  * @constructor
27437  * Create a new ComboBox.
27438  * @param {Object} config Configuration options
27439  */
27440 Roo.form.Select = function(config){
27441     Roo.form.Select.superclass.constructor.call(this, config);
27442      
27443 };
27444
27445 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
27446     /**
27447      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
27448      */
27449     /**
27450      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
27451      * rendering into an Roo.Editor, defaults to false)
27452      */
27453     /**
27454      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
27455      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
27456      */
27457     /**
27458      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
27459      */
27460     /**
27461      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
27462      * the dropdown list (defaults to undefined, with no header element)
27463      */
27464
27465      /**
27466      * @cfg {String/Roo.Template} tpl The template to use to render the output
27467      */
27468      
27469     // private
27470     defaultAutoCreate : {tag: "select"  },
27471     /**
27472      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
27473      */
27474     listWidth: undefined,
27475     /**
27476      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
27477      * mode = 'remote' or 'text' if mode = 'local')
27478      */
27479     displayField: undefined,
27480     /**
27481      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
27482      * mode = 'remote' or 'value' if mode = 'local'). 
27483      * Note: use of a valueField requires the user make a selection
27484      * in order for a value to be mapped.
27485      */
27486     valueField: undefined,
27487     
27488     
27489     /**
27490      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
27491      * field's data value (defaults to the underlying DOM element's name)
27492      */
27493     hiddenName: undefined,
27494     /**
27495      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
27496      */
27497     listClass: '',
27498     /**
27499      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
27500      */
27501     selectedClass: 'x-combo-selected',
27502     /**
27503      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
27504      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
27505      * which displays a downward arrow icon).
27506      */
27507     triggerClass : 'x-form-arrow-trigger',
27508     /**
27509      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
27510      */
27511     shadow:'sides',
27512     /**
27513      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
27514      * anchor positions (defaults to 'tl-bl')
27515      */
27516     listAlign: 'tl-bl?',
27517     /**
27518      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
27519      */
27520     maxHeight: 300,
27521     /**
27522      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
27523      * query specified by the allQuery config option (defaults to 'query')
27524      */
27525     triggerAction: 'query',
27526     /**
27527      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
27528      * (defaults to 4, does not apply if editable = false)
27529      */
27530     minChars : 4,
27531     /**
27532      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
27533      * delay (typeAheadDelay) if it matches a known value (defaults to false)
27534      */
27535     typeAhead: false,
27536     /**
27537      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
27538      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
27539      */
27540     queryDelay: 500,
27541     /**
27542      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
27543      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
27544      */
27545     pageSize: 0,
27546     /**
27547      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
27548      * when editable = true (defaults to false)
27549      */
27550     selectOnFocus:false,
27551     /**
27552      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
27553      */
27554     queryParam: 'query',
27555     /**
27556      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
27557      * when mode = 'remote' (defaults to 'Loading...')
27558      */
27559     loadingText: 'Loading...',
27560     /**
27561      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
27562      */
27563     resizable: false,
27564     /**
27565      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
27566      */
27567     handleHeight : 8,
27568     /**
27569      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
27570      * traditional select (defaults to true)
27571      */
27572     editable: true,
27573     /**
27574      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
27575      */
27576     allQuery: '',
27577     /**
27578      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
27579      */
27580     mode: 'remote',
27581     /**
27582      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
27583      * listWidth has a higher value)
27584      */
27585     minListWidth : 70,
27586     /**
27587      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
27588      * allow the user to set arbitrary text into the field (defaults to false)
27589      */
27590     forceSelection:false,
27591     /**
27592      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
27593      * if typeAhead = true (defaults to 250)
27594      */
27595     typeAheadDelay : 250,
27596     /**
27597      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
27598      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
27599      */
27600     valueNotFoundText : undefined,
27601     
27602     /**
27603      * @cfg {String} defaultValue The value displayed after loading the store.
27604      */
27605     defaultValue: '',
27606     
27607     /**
27608      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
27609      */
27610     blockFocus : false,
27611     
27612     /**
27613      * @cfg {Boolean} disableClear Disable showing of clear button.
27614      */
27615     disableClear : false,
27616     /**
27617      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
27618      */
27619     alwaysQuery : false,
27620     
27621     //private
27622     addicon : false,
27623     editicon: false,
27624     
27625     // element that contains real text value.. (when hidden is used..)
27626      
27627     // private
27628     onRender : function(ct, position){
27629         Roo.form.Field.prototype.onRender.call(this, ct, position);
27630         
27631         if(this.store){
27632             this.store.on('beforeload', this.onBeforeLoad, this);
27633             this.store.on('load', this.onLoad, this);
27634             this.store.on('loadexception', this.onLoadException, this);
27635             this.store.load({});
27636         }
27637         
27638         
27639         
27640     },
27641
27642     // private
27643     initEvents : function(){
27644         //Roo.form.ComboBox.superclass.initEvents.call(this);
27645  
27646     },
27647
27648     onDestroy : function(){
27649        
27650         if(this.store){
27651             this.store.un('beforeload', this.onBeforeLoad, this);
27652             this.store.un('load', this.onLoad, this);
27653             this.store.un('loadexception', this.onLoadException, this);
27654         }
27655         //Roo.form.ComboBox.superclass.onDestroy.call(this);
27656     },
27657
27658     // private
27659     fireKey : function(e){
27660         if(e.isNavKeyPress() && !this.list.isVisible()){
27661             this.fireEvent("specialkey", this, e);
27662         }
27663     },
27664
27665     // private
27666     onResize: function(w, h){
27667         
27668         return; 
27669     
27670         
27671     },
27672
27673     /**
27674      * Allow or prevent the user from directly editing the field text.  If false is passed,
27675      * the user will only be able to select from the items defined in the dropdown list.  This method
27676      * is the runtime equivalent of setting the 'editable' config option at config time.
27677      * @param {Boolean} value True to allow the user to directly edit the field text
27678      */
27679     setEditable : function(value){
27680          
27681     },
27682
27683     // private
27684     onBeforeLoad : function(){
27685         
27686         Roo.log("Select before load");
27687         return;
27688     
27689         this.innerList.update(this.loadingText ?
27690                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
27691         //this.restrictHeight();
27692         this.selectedIndex = -1;
27693     },
27694
27695     // private
27696     onLoad : function(){
27697
27698     
27699         var dom = this.el.dom;
27700         dom.innerHTML = '';
27701          var od = dom.ownerDocument;
27702          
27703         if (this.emptyText) {
27704             var op = od.createElement('option');
27705             op.setAttribute('value', '');
27706             op.innerHTML = String.format('{0}', this.emptyText);
27707             dom.appendChild(op);
27708         }
27709         if(this.store.getCount() > 0){
27710            
27711             var vf = this.valueField;
27712             var df = this.displayField;
27713             this.store.data.each(function(r) {
27714                 // which colmsn to use... testing - cdoe / title..
27715                 var op = od.createElement('option');
27716                 op.setAttribute('value', r.data[vf]);
27717                 op.innerHTML = String.format('{0}', r.data[df]);
27718                 dom.appendChild(op);
27719             });
27720             if (typeof(this.defaultValue != 'undefined')) {
27721                 this.setValue(this.defaultValue);
27722             }
27723             
27724              
27725         }else{
27726             //this.onEmptyResults();
27727         }
27728         //this.el.focus();
27729     },
27730     // private
27731     onLoadException : function()
27732     {
27733         dom.innerHTML = '';
27734             
27735         Roo.log("Select on load exception");
27736         return;
27737     
27738         this.collapse();
27739         Roo.log(this.store.reader.jsonData);
27740         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
27741             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
27742         }
27743         
27744         
27745     },
27746     // private
27747     onTypeAhead : function(){
27748          
27749     },
27750
27751     // private
27752     onSelect : function(record, index){
27753         Roo.log('on select?');
27754         return;
27755         if(this.fireEvent('beforeselect', this, record, index) !== false){
27756             this.setFromData(index > -1 ? record.data : false);
27757             this.collapse();
27758             this.fireEvent('select', this, record, index);
27759         }
27760     },
27761
27762     /**
27763      * Returns the currently selected field value or empty string if no value is set.
27764      * @return {String} value The selected value
27765      */
27766     getValue : function(){
27767         var dom = this.el.dom;
27768         this.value = dom.options[dom.selectedIndex].value;
27769         return this.value;
27770         
27771     },
27772
27773     /**
27774      * Clears any text/value currently set in the field
27775      */
27776     clearValue : function(){
27777         this.value = '';
27778         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
27779         
27780     },
27781
27782     /**
27783      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
27784      * will be displayed in the field.  If the value does not match the data value of an existing item,
27785      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
27786      * Otherwise the field will be blank (although the value will still be set).
27787      * @param {String} value The value to match
27788      */
27789     setValue : function(v){
27790         var d = this.el.dom;
27791         for (var i =0; i < d.options.length;i++) {
27792             if (v == d.options[i].value) {
27793                 d.selectedIndex = i;
27794                 this.value = v;
27795                 return;
27796             }
27797         }
27798         this.clearValue();
27799     },
27800     /**
27801      * @property {Object} the last set data for the element
27802      */
27803     
27804     lastData : false,
27805     /**
27806      * Sets the value of the field based on a object which is related to the record format for the store.
27807      * @param {Object} value the value to set as. or false on reset?
27808      */
27809     setFromData : function(o){
27810         Roo.log('setfrom data?');
27811          
27812         
27813         
27814     },
27815     // private
27816     reset : function(){
27817         this.clearValue();
27818     },
27819     // private
27820     findRecord : function(prop, value){
27821         
27822         return false;
27823     
27824         var record;
27825         if(this.store.getCount() > 0){
27826             this.store.each(function(r){
27827                 if(r.data[prop] == value){
27828                     record = r;
27829                     return false;
27830                 }
27831                 return true;
27832             });
27833         }
27834         return record;
27835     },
27836     
27837     getName: function()
27838     {
27839         // returns hidden if it's set..
27840         if (!this.rendered) {return ''};
27841         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
27842         
27843     },
27844      
27845
27846     
27847
27848     // private
27849     onEmptyResults : function(){
27850         Roo.log('empty results');
27851         //this.collapse();
27852     },
27853
27854     /**
27855      * Returns true if the dropdown list is expanded, else false.
27856      */
27857     isExpanded : function(){
27858         return false;
27859     },
27860
27861     /**
27862      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
27863      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
27864      * @param {String} value The data value of the item to select
27865      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
27866      * selected item if it is not currently in view (defaults to true)
27867      * @return {Boolean} True if the value matched an item in the list, else false
27868      */
27869     selectByValue : function(v, scrollIntoView){
27870         Roo.log('select By Value');
27871         return false;
27872     
27873         if(v !== undefined && v !== null){
27874             var r = this.findRecord(this.valueField || this.displayField, v);
27875             if(r){
27876                 this.select(this.store.indexOf(r), scrollIntoView);
27877                 return true;
27878             }
27879         }
27880         return false;
27881     },
27882
27883     /**
27884      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
27885      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
27886      * @param {Number} index The zero-based index of the list item to select
27887      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
27888      * selected item if it is not currently in view (defaults to true)
27889      */
27890     select : function(index, scrollIntoView){
27891         Roo.log('select ');
27892         return  ;
27893         
27894         this.selectedIndex = index;
27895         this.view.select(index);
27896         if(scrollIntoView !== false){
27897             var el = this.view.getNode(index);
27898             if(el){
27899                 this.innerList.scrollChildIntoView(el, false);
27900             }
27901         }
27902     },
27903
27904       
27905
27906     // private
27907     validateBlur : function(){
27908         
27909         return;
27910         
27911     },
27912
27913     // private
27914     initQuery : function(){
27915         this.doQuery(this.getRawValue());
27916     },
27917
27918     // private
27919     doForce : function(){
27920         if(this.el.dom.value.length > 0){
27921             this.el.dom.value =
27922                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
27923              
27924         }
27925     },
27926
27927     /**
27928      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
27929      * query allowing the query action to be canceled if needed.
27930      * @param {String} query The SQL query to execute
27931      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
27932      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
27933      * saved in the current store (defaults to false)
27934      */
27935     doQuery : function(q, forceAll){
27936         
27937         Roo.log('doQuery?');
27938         if(q === undefined || q === null){
27939             q = '';
27940         }
27941         var qe = {
27942             query: q,
27943             forceAll: forceAll,
27944             combo: this,
27945             cancel:false
27946         };
27947         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
27948             return false;
27949         }
27950         q = qe.query;
27951         forceAll = qe.forceAll;
27952         if(forceAll === true || (q.length >= this.minChars)){
27953             if(this.lastQuery != q || this.alwaysQuery){
27954                 this.lastQuery = q;
27955                 if(this.mode == 'local'){
27956                     this.selectedIndex = -1;
27957                     if(forceAll){
27958                         this.store.clearFilter();
27959                     }else{
27960                         this.store.filter(this.displayField, q);
27961                     }
27962                     this.onLoad();
27963                 }else{
27964                     this.store.baseParams[this.queryParam] = q;
27965                     this.store.load({
27966                         params: this.getParams(q)
27967                     });
27968                     this.expand();
27969                 }
27970             }else{
27971                 this.selectedIndex = -1;
27972                 this.onLoad();   
27973             }
27974         }
27975     },
27976
27977     // private
27978     getParams : function(q){
27979         var p = {};
27980         //p[this.queryParam] = q;
27981         if(this.pageSize){
27982             p.start = 0;
27983             p.limit = this.pageSize;
27984         }
27985         return p;
27986     },
27987
27988     /**
27989      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
27990      */
27991     collapse : function(){
27992         
27993     },
27994
27995     // private
27996     collapseIf : function(e){
27997         
27998     },
27999
28000     /**
28001      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28002      */
28003     expand : function(){
28004         
28005     } ,
28006
28007     // private
28008      
28009
28010     /** 
28011     * @cfg {Boolean} grow 
28012     * @hide 
28013     */
28014     /** 
28015     * @cfg {Number} growMin 
28016     * @hide 
28017     */
28018     /** 
28019     * @cfg {Number} growMax 
28020     * @hide 
28021     */
28022     /**
28023      * @hide
28024      * @method autoSize
28025      */
28026     
28027     setWidth : function()
28028     {
28029         
28030     },
28031     getResizeEl : function(){
28032         return this.el;
28033     }
28034 });//<script type="text/javasscript">
28035  
28036
28037 /**
28038  * @class Roo.DDView
28039  * A DnD enabled version of Roo.View.
28040  * @param {Element/String} container The Element in which to create the View.
28041  * @param {String} tpl The template string used to create the markup for each element of the View
28042  * @param {Object} config The configuration properties. These include all the config options of
28043  * {@link Roo.View} plus some specific to this class.<br>
28044  * <p>
28045  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28046  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28047  * <p>
28048  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28049 .x-view-drag-insert-above {
28050         border-top:1px dotted #3366cc;
28051 }
28052 .x-view-drag-insert-below {
28053         border-bottom:1px dotted #3366cc;
28054 }
28055 </code></pre>
28056  * 
28057  */
28058  
28059 Roo.DDView = function(container, tpl, config) {
28060     Roo.DDView.superclass.constructor.apply(this, arguments);
28061     this.getEl().setStyle("outline", "0px none");
28062     this.getEl().unselectable();
28063     if (this.dragGroup) {
28064                 this.setDraggable(this.dragGroup.split(","));
28065     }
28066     if (this.dropGroup) {
28067                 this.setDroppable(this.dropGroup.split(","));
28068     }
28069     if (this.deletable) {
28070         this.setDeletable();
28071     }
28072     this.isDirtyFlag = false;
28073         this.addEvents({
28074                 "drop" : true
28075         });
28076 };
28077
28078 Roo.extend(Roo.DDView, Roo.View, {
28079 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28080 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28081 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28082 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28083
28084         isFormField: true,
28085
28086         reset: Roo.emptyFn,
28087         
28088         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28089
28090         validate: function() {
28091                 return true;
28092         },
28093         
28094         destroy: function() {
28095                 this.purgeListeners();
28096                 this.getEl.removeAllListeners();
28097                 this.getEl().remove();
28098                 if (this.dragZone) {
28099                         if (this.dragZone.destroy) {
28100                                 this.dragZone.destroy();
28101                         }
28102                 }
28103                 if (this.dropZone) {
28104                         if (this.dropZone.destroy) {
28105                                 this.dropZone.destroy();
28106                         }
28107                 }
28108         },
28109
28110 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28111         getName: function() {
28112                 return this.name;
28113         },
28114
28115 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28116         setValue: function(v) {
28117                 if (!this.store) {
28118                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28119                 }
28120                 var data = {};
28121                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28122                 this.store.proxy = new Roo.data.MemoryProxy(data);
28123                 this.store.load();
28124         },
28125
28126 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28127         getValue: function() {
28128                 var result = '(';
28129                 this.store.each(function(rec) {
28130                         result += rec.id + ',';
28131                 });
28132                 return result.substr(0, result.length - 1) + ')';
28133         },
28134         
28135         getIds: function() {
28136                 var i = 0, result = new Array(this.store.getCount());
28137                 this.store.each(function(rec) {
28138                         result[i++] = rec.id;
28139                 });
28140                 return result;
28141         },
28142         
28143         isDirty: function() {
28144                 return this.isDirtyFlag;
28145         },
28146
28147 /**
28148  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28149  *      whole Element becomes the target, and this causes the drop gesture to append.
28150  */
28151     getTargetFromEvent : function(e) {
28152                 var target = e.getTarget();
28153                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28154                 target = target.parentNode;
28155                 }
28156                 if (!target) {
28157                         target = this.el.dom.lastChild || this.el.dom;
28158                 }
28159                 return target;
28160     },
28161
28162 /**
28163  *      Create the drag data which consists of an object which has the property "ddel" as
28164  *      the drag proxy element. 
28165  */
28166     getDragData : function(e) {
28167         var target = this.findItemFromChild(e.getTarget());
28168                 if(target) {
28169                         this.handleSelection(e);
28170                         var selNodes = this.getSelectedNodes();
28171             var dragData = {
28172                 source: this,
28173                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28174                 nodes: selNodes,
28175                 records: []
28176                         };
28177                         var selectedIndices = this.getSelectedIndexes();
28178                         for (var i = 0; i < selectedIndices.length; i++) {
28179                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28180                         }
28181                         if (selNodes.length == 1) {
28182                                 dragData.ddel = target.cloneNode(true); // the div element
28183                         } else {
28184                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28185                                 div.className = 'multi-proxy';
28186                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28187                                         div.appendChild(selNodes[i].cloneNode(true));
28188                                 }
28189                                 dragData.ddel = div;
28190                         }
28191             //console.log(dragData)
28192             //console.log(dragData.ddel.innerHTML)
28193                         return dragData;
28194                 }
28195         //console.log('nodragData')
28196                 return false;
28197     },
28198     
28199 /**     Specify to which ddGroup items in this DDView may be dragged. */
28200     setDraggable: function(ddGroup) {
28201         if (ddGroup instanceof Array) {
28202                 Roo.each(ddGroup, this.setDraggable, this);
28203                 return;
28204         }
28205         if (this.dragZone) {
28206                 this.dragZone.addToGroup(ddGroup);
28207         } else {
28208                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28209                                 containerScroll: true,
28210                                 ddGroup: ddGroup 
28211
28212                         });
28213 //                      Draggability implies selection. DragZone's mousedown selects the element.
28214                         if (!this.multiSelect) { this.singleSelect = true; }
28215
28216 //                      Wire the DragZone's handlers up to methods in *this*
28217                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28218                 }
28219     },
28220
28221 /**     Specify from which ddGroup this DDView accepts drops. */
28222     setDroppable: function(ddGroup) {
28223         if (ddGroup instanceof Array) {
28224                 Roo.each(ddGroup, this.setDroppable, this);
28225                 return;
28226         }
28227         if (this.dropZone) {
28228                 this.dropZone.addToGroup(ddGroup);
28229         } else {
28230                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28231                                 containerScroll: true,
28232                                 ddGroup: ddGroup
28233                         });
28234
28235 //                      Wire the DropZone's handlers up to methods in *this*
28236                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28237                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28238                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28239                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28240                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28241                 }
28242     },
28243
28244 /**     Decide whether to drop above or below a View node. */
28245     getDropPoint : function(e, n, dd){
28246         if (n == this.el.dom) { return "above"; }
28247                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28248                 var c = t + (b - t) / 2;
28249                 var y = Roo.lib.Event.getPageY(e);
28250                 if(y <= c) {
28251                         return "above";
28252                 }else{
28253                         return "below";
28254                 }
28255     },
28256
28257     onNodeEnter : function(n, dd, e, data){
28258                 return false;
28259     },
28260     
28261     onNodeOver : function(n, dd, e, data){
28262                 var pt = this.getDropPoint(e, n, dd);
28263                 // set the insert point style on the target node
28264                 var dragElClass = this.dropNotAllowed;
28265                 if (pt) {
28266                         var targetElClass;
28267                         if (pt == "above"){
28268                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28269                                 targetElClass = "x-view-drag-insert-above";
28270                         } else {
28271                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28272                                 targetElClass = "x-view-drag-insert-below";
28273                         }
28274                         if (this.lastInsertClass != targetElClass){
28275                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28276                                 this.lastInsertClass = targetElClass;
28277                         }
28278                 }
28279                 return dragElClass;
28280         },
28281
28282     onNodeOut : function(n, dd, e, data){
28283                 this.removeDropIndicators(n);
28284     },
28285
28286     onNodeDrop : function(n, dd, e, data){
28287         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28288                 return false;
28289         }
28290         var pt = this.getDropPoint(e, n, dd);
28291                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28292                 if (pt == "below") { insertAt++; }
28293                 for (var i = 0; i < data.records.length; i++) {
28294                         var r = data.records[i];
28295                         var dup = this.store.getById(r.id);
28296                         if (dup && (dd != this.dragZone)) {
28297                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28298                         } else {
28299                                 if (data.copy) {
28300                                         this.store.insert(insertAt++, r.copy());
28301                                 } else {
28302                                         data.source.isDirtyFlag = true;
28303                                         r.store.remove(r);
28304                                         this.store.insert(insertAt++, r);
28305                                 }
28306                                 this.isDirtyFlag = true;
28307                         }
28308                 }
28309                 this.dragZone.cachedTarget = null;
28310                 return true;
28311     },
28312
28313     removeDropIndicators : function(n){
28314                 if(n){
28315                         Roo.fly(n).removeClass([
28316                                 "x-view-drag-insert-above",
28317                                 "x-view-drag-insert-below"]);
28318                         this.lastInsertClass = "_noclass";
28319                 }
28320     },
28321
28322 /**
28323  *      Utility method. Add a delete option to the DDView's context menu.
28324  *      @param {String} imageUrl The URL of the "delete" icon image.
28325  */
28326         setDeletable: function(imageUrl) {
28327                 if (!this.singleSelect && !this.multiSelect) {
28328                         this.singleSelect = true;
28329                 }
28330                 var c = this.getContextMenu();
28331                 this.contextMenu.on("itemclick", function(item) {
28332                         switch (item.id) {
28333                                 case "delete":
28334                                         this.remove(this.getSelectedIndexes());
28335                                         break;
28336                         }
28337                 }, this);
28338                 this.contextMenu.add({
28339                         icon: imageUrl,
28340                         id: "delete",
28341                         text: 'Delete'
28342                 });
28343         },
28344         
28345 /**     Return the context menu for this DDView. */
28346         getContextMenu: function() {
28347                 if (!this.contextMenu) {
28348 //                      Create the View's context menu
28349                         this.contextMenu = new Roo.menu.Menu({
28350                                 id: this.id + "-contextmenu"
28351                         });
28352                         this.el.on("contextmenu", this.showContextMenu, this);
28353                 }
28354                 return this.contextMenu;
28355         },
28356         
28357         disableContextMenu: function() {
28358                 if (this.contextMenu) {
28359                         this.el.un("contextmenu", this.showContextMenu, this);
28360                 }
28361         },
28362
28363         showContextMenu: function(e, item) {
28364         item = this.findItemFromChild(e.getTarget());
28365                 if (item) {
28366                         e.stopEvent();
28367                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28368                         this.contextMenu.showAt(e.getXY());
28369             }
28370     },
28371
28372 /**
28373  *      Remove {@link Roo.data.Record}s at the specified indices.
28374  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28375  */
28376     remove: function(selectedIndices) {
28377                 selectedIndices = [].concat(selectedIndices);
28378                 for (var i = 0; i < selectedIndices.length; i++) {
28379                         var rec = this.store.getAt(selectedIndices[i]);
28380                         this.store.remove(rec);
28381                 }
28382     },
28383
28384 /**
28385  *      Double click fires the event, but also, if this is draggable, and there is only one other
28386  *      related DropZone, it transfers the selected node.
28387  */
28388     onDblClick : function(e){
28389         var item = this.findItemFromChild(e.getTarget());
28390         if(item){
28391             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28392                 return false;
28393             }
28394             if (this.dragGroup) {
28395                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28396                     while (targets.indexOf(this.dropZone) > -1) {
28397                             targets.remove(this.dropZone);
28398                                 }
28399                     if (targets.length == 1) {
28400                                         this.dragZone.cachedTarget = null;
28401                         var el = Roo.get(targets[0].getEl());
28402                         var box = el.getBox(true);
28403                         targets[0].onNodeDrop(el.dom, {
28404                                 target: el.dom,
28405                                 xy: [box.x, box.y + box.height - 1]
28406                         }, null, this.getDragData(e));
28407                     }
28408                 }
28409         }
28410     },
28411     
28412     handleSelection: function(e) {
28413                 this.dragZone.cachedTarget = null;
28414         var item = this.findItemFromChild(e.getTarget());
28415         if (!item) {
28416                 this.clearSelections(true);
28417                 return;
28418         }
28419                 if (item && (this.multiSelect || this.singleSelect)){
28420                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28421                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28422                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28423                                 this.unselect(item);
28424                         } else {
28425                                 this.select(item, this.multiSelect && e.ctrlKey);
28426                                 this.lastSelection = item;
28427                         }
28428                 }
28429     },
28430
28431     onItemClick : function(item, index, e){
28432                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28433                         return false;
28434                 }
28435                 return true;
28436     },
28437
28438     unselect : function(nodeInfo, suppressEvent){
28439                 var node = this.getNode(nodeInfo);
28440                 if(node && this.isSelected(node)){
28441                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28442                                 Roo.fly(node).removeClass(this.selectedClass);
28443                                 this.selections.remove(node);
28444                                 if(!suppressEvent){
28445                                         this.fireEvent("selectionchange", this, this.selections);
28446                                 }
28447                         }
28448                 }
28449     }
28450 });
28451 /*
28452  * Based on:
28453  * Ext JS Library 1.1.1
28454  * Copyright(c) 2006-2007, Ext JS, LLC.
28455  *
28456  * Originally Released Under LGPL - original licence link has changed is not relivant.
28457  *
28458  * Fork - LGPL
28459  * <script type="text/javascript">
28460  */
28461  
28462 /**
28463  * @class Roo.LayoutManager
28464  * @extends Roo.util.Observable
28465  * Base class for layout managers.
28466  */
28467 Roo.LayoutManager = function(container, config){
28468     Roo.LayoutManager.superclass.constructor.call(this);
28469     this.el = Roo.get(container);
28470     // ie scrollbar fix
28471     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28472         document.body.scroll = "no";
28473     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28474         this.el.position('relative');
28475     }
28476     this.id = this.el.id;
28477     this.el.addClass("x-layout-container");
28478     /** false to disable window resize monitoring @type Boolean */
28479     this.monitorWindowResize = true;
28480     this.regions = {};
28481     this.addEvents({
28482         /**
28483          * @event layout
28484          * Fires when a layout is performed. 
28485          * @param {Roo.LayoutManager} this
28486          */
28487         "layout" : true,
28488         /**
28489          * @event regionresized
28490          * Fires when the user resizes a region. 
28491          * @param {Roo.LayoutRegion} region The resized region
28492          * @param {Number} newSize The new size (width for east/west, height for north/south)
28493          */
28494         "regionresized" : true,
28495         /**
28496          * @event regioncollapsed
28497          * Fires when a region is collapsed. 
28498          * @param {Roo.LayoutRegion} region The collapsed region
28499          */
28500         "regioncollapsed" : true,
28501         /**
28502          * @event regionexpanded
28503          * Fires when a region is expanded.  
28504          * @param {Roo.LayoutRegion} region The expanded region
28505          */
28506         "regionexpanded" : true
28507     });
28508     this.updating = false;
28509     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28510 };
28511
28512 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28513     /**
28514      * Returns true if this layout is currently being updated
28515      * @return {Boolean}
28516      */
28517     isUpdating : function(){
28518         return this.updating; 
28519     },
28520     
28521     /**
28522      * Suspend the LayoutManager from doing auto-layouts while
28523      * making multiple add or remove calls
28524      */
28525     beginUpdate : function(){
28526         this.updating = true;    
28527     },
28528     
28529     /**
28530      * Restore auto-layouts and optionally disable the manager from performing a layout
28531      * @param {Boolean} noLayout true to disable a layout update 
28532      */
28533     endUpdate : function(noLayout){
28534         this.updating = false;
28535         if(!noLayout){
28536             this.layout();
28537         }    
28538     },
28539     
28540     layout: function(){
28541         
28542     },
28543     
28544     onRegionResized : function(region, newSize){
28545         this.fireEvent("regionresized", region, newSize);
28546         this.layout();
28547     },
28548     
28549     onRegionCollapsed : function(region){
28550         this.fireEvent("regioncollapsed", region);
28551     },
28552     
28553     onRegionExpanded : function(region){
28554         this.fireEvent("regionexpanded", region);
28555     },
28556         
28557     /**
28558      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28559      * performs box-model adjustments.
28560      * @return {Object} The size as an object {width: (the width), height: (the height)}
28561      */
28562     getViewSize : function(){
28563         var size;
28564         if(this.el.dom != document.body){
28565             size = this.el.getSize();
28566         }else{
28567             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28568         }
28569         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28570         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28571         return size;
28572     },
28573     
28574     /**
28575      * Returns the Element this layout is bound to.
28576      * @return {Roo.Element}
28577      */
28578     getEl : function(){
28579         return this.el;
28580     },
28581     
28582     /**
28583      * Returns the specified region.
28584      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
28585      * @return {Roo.LayoutRegion}
28586      */
28587     getRegion : function(target){
28588         return this.regions[target.toLowerCase()];
28589     },
28590     
28591     onWindowResize : function(){
28592         if(this.monitorWindowResize){
28593             this.layout();
28594         }
28595     }
28596 });/*
28597  * Based on:
28598  * Ext JS Library 1.1.1
28599  * Copyright(c) 2006-2007, Ext JS, LLC.
28600  *
28601  * Originally Released Under LGPL - original licence link has changed is not relivant.
28602  *
28603  * Fork - LGPL
28604  * <script type="text/javascript">
28605  */
28606 /**
28607  * @class Roo.BorderLayout
28608  * @extends Roo.LayoutManager
28609  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
28610  * please see: <br><br>
28611  * <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>
28612  * <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>
28613  * Example:
28614  <pre><code>
28615  var layout = new Roo.BorderLayout(document.body, {
28616     north: {
28617         initialSize: 25,
28618         titlebar: false
28619     },
28620     west: {
28621         split:true,
28622         initialSize: 200,
28623         minSize: 175,
28624         maxSize: 400,
28625         titlebar: true,
28626         collapsible: true
28627     },
28628     east: {
28629         split:true,
28630         initialSize: 202,
28631         minSize: 175,
28632         maxSize: 400,
28633         titlebar: true,
28634         collapsible: true
28635     },
28636     south: {
28637         split:true,
28638         initialSize: 100,
28639         minSize: 100,
28640         maxSize: 200,
28641         titlebar: true,
28642         collapsible: true
28643     },
28644     center: {
28645         titlebar: true,
28646         autoScroll:true,
28647         resizeTabs: true,
28648         minTabWidth: 50,
28649         preferredTabWidth: 150
28650     }
28651 });
28652
28653 // shorthand
28654 var CP = Roo.ContentPanel;
28655
28656 layout.beginUpdate();
28657 layout.add("north", new CP("north", "North"));
28658 layout.add("south", new CP("south", {title: "South", closable: true}));
28659 layout.add("west", new CP("west", {title: "West"}));
28660 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
28661 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
28662 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
28663 layout.getRegion("center").showPanel("center1");
28664 layout.endUpdate();
28665 </code></pre>
28666
28667 <b>The container the layout is rendered into can be either the body element or any other element.
28668 If it is not the body element, the container needs to either be an absolute positioned element,
28669 or you will need to add "position:relative" to the css of the container.  You will also need to specify
28670 the container size if it is not the body element.</b>
28671
28672 * @constructor
28673 * Create a new BorderLayout
28674 * @param {String/HTMLElement/Element} container The container this layout is bound to
28675 * @param {Object} config Configuration options
28676  */
28677 Roo.BorderLayout = function(container, config){
28678     config = config || {};
28679     Roo.BorderLayout.superclass.constructor.call(this, container, config);
28680     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
28681     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
28682         var target = this.factory.validRegions[i];
28683         if(config[target]){
28684             this.addRegion(target, config[target]);
28685         }
28686     }
28687 };
28688
28689 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
28690     /**
28691      * Creates and adds a new region if it doesn't already exist.
28692      * @param {String} target The target region key (north, south, east, west or center).
28693      * @param {Object} config The regions config object
28694      * @return {BorderLayoutRegion} The new region
28695      */
28696     addRegion : function(target, config){
28697         if(!this.regions[target]){
28698             var r = this.factory.create(target, this, config);
28699             this.bindRegion(target, r);
28700         }
28701         return this.regions[target];
28702     },
28703
28704     // private (kinda)
28705     bindRegion : function(name, r){
28706         this.regions[name] = r;
28707         r.on("visibilitychange", this.layout, this);
28708         r.on("paneladded", this.layout, this);
28709         r.on("panelremoved", this.layout, this);
28710         r.on("invalidated", this.layout, this);
28711         r.on("resized", this.onRegionResized, this);
28712         r.on("collapsed", this.onRegionCollapsed, this);
28713         r.on("expanded", this.onRegionExpanded, this);
28714     },
28715
28716     /**
28717      * Performs a layout update.
28718      */
28719     layout : function(){
28720         if(this.updating) {
28721             return;
28722         }
28723         var size = this.getViewSize();
28724         var w = size.width;
28725         var h = size.height;
28726         var centerW = w;
28727         var centerH = h;
28728         var centerY = 0;
28729         var centerX = 0;
28730         //var x = 0, y = 0;
28731
28732         var rs = this.regions;
28733         var north = rs["north"];
28734         var south = rs["south"]; 
28735         var west = rs["west"];
28736         var east = rs["east"];
28737         var center = rs["center"];
28738         //if(this.hideOnLayout){ // not supported anymore
28739             //c.el.setStyle("display", "none");
28740         //}
28741         if(north && north.isVisible()){
28742             var b = north.getBox();
28743             var m = north.getMargins();
28744             b.width = w - (m.left+m.right);
28745             b.x = m.left;
28746             b.y = m.top;
28747             centerY = b.height + b.y + m.bottom;
28748             centerH -= centerY;
28749             north.updateBox(this.safeBox(b));
28750         }
28751         if(south && south.isVisible()){
28752             var b = south.getBox();
28753             var m = south.getMargins();
28754             b.width = w - (m.left+m.right);
28755             b.x = m.left;
28756             var totalHeight = (b.height + m.top + m.bottom);
28757             b.y = h - totalHeight + m.top;
28758             centerH -= totalHeight;
28759             south.updateBox(this.safeBox(b));
28760         }
28761         if(west && west.isVisible()){
28762             var b = west.getBox();
28763             var m = west.getMargins();
28764             b.height = centerH - (m.top+m.bottom);
28765             b.x = m.left;
28766             b.y = centerY + m.top;
28767             var totalWidth = (b.width + m.left + m.right);
28768             centerX += totalWidth;
28769             centerW -= totalWidth;
28770             west.updateBox(this.safeBox(b));
28771         }
28772         if(east && east.isVisible()){
28773             var b = east.getBox();
28774             var m = east.getMargins();
28775             b.height = centerH - (m.top+m.bottom);
28776             var totalWidth = (b.width + m.left + m.right);
28777             b.x = w - totalWidth + m.left;
28778             b.y = centerY + m.top;
28779             centerW -= totalWidth;
28780             east.updateBox(this.safeBox(b));
28781         }
28782         if(center){
28783             var m = center.getMargins();
28784             var centerBox = {
28785                 x: centerX + m.left,
28786                 y: centerY + m.top,
28787                 width: centerW - (m.left+m.right),
28788                 height: centerH - (m.top+m.bottom)
28789             };
28790             //if(this.hideOnLayout){
28791                 //center.el.setStyle("display", "block");
28792             //}
28793             center.updateBox(this.safeBox(centerBox));
28794         }
28795         this.el.repaint();
28796         this.fireEvent("layout", this);
28797     },
28798
28799     // private
28800     safeBox : function(box){
28801         box.width = Math.max(0, box.width);
28802         box.height = Math.max(0, box.height);
28803         return box;
28804     },
28805
28806     /**
28807      * Adds a ContentPanel (or subclass) to this layout.
28808      * @param {String} target The target region key (north, south, east, west or center).
28809      * @param {Roo.ContentPanel} panel The panel to add
28810      * @return {Roo.ContentPanel} The added panel
28811      */
28812     add : function(target, panel){
28813          
28814         target = target.toLowerCase();
28815         return this.regions[target].add(panel);
28816     },
28817
28818     /**
28819      * Remove a ContentPanel (or subclass) to this layout.
28820      * @param {String} target The target region key (north, south, east, west or center).
28821      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
28822      * @return {Roo.ContentPanel} The removed panel
28823      */
28824     remove : function(target, panel){
28825         target = target.toLowerCase();
28826         return this.regions[target].remove(panel);
28827     },
28828
28829     /**
28830      * Searches all regions for a panel with the specified id
28831      * @param {String} panelId
28832      * @return {Roo.ContentPanel} The panel or null if it wasn't found
28833      */
28834     findPanel : function(panelId){
28835         var rs = this.regions;
28836         for(var target in rs){
28837             if(typeof rs[target] != "function"){
28838                 var p = rs[target].getPanel(panelId);
28839                 if(p){
28840                     return p;
28841                 }
28842             }
28843         }
28844         return null;
28845     },
28846
28847     /**
28848      * Searches all regions for a panel with the specified id and activates (shows) it.
28849      * @param {String/ContentPanel} panelId The panels id or the panel itself
28850      * @return {Roo.ContentPanel} The shown panel or null
28851      */
28852     showPanel : function(panelId) {
28853       var rs = this.regions;
28854       for(var target in rs){
28855          var r = rs[target];
28856          if(typeof r != "function"){
28857             if(r.hasPanel(panelId)){
28858                return r.showPanel(panelId);
28859             }
28860          }
28861       }
28862       return null;
28863    },
28864
28865    /**
28866      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
28867      * @param {Roo.state.Provider} provider (optional) An alternate state provider
28868      */
28869     restoreState : function(provider){
28870         if(!provider){
28871             provider = Roo.state.Manager;
28872         }
28873         var sm = new Roo.LayoutStateManager();
28874         sm.init(this, provider);
28875     },
28876
28877     /**
28878      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
28879      * object should contain properties for each region to add ContentPanels to, and each property's value should be
28880      * a valid ContentPanel config object.  Example:
28881      * <pre><code>
28882 // Create the main layout
28883 var layout = new Roo.BorderLayout('main-ct', {
28884     west: {
28885         split:true,
28886         minSize: 175,
28887         titlebar: true
28888     },
28889     center: {
28890         title:'Components'
28891     }
28892 }, 'main-ct');
28893
28894 // Create and add multiple ContentPanels at once via configs
28895 layout.batchAdd({
28896    west: {
28897        id: 'source-files',
28898        autoCreate:true,
28899        title:'Ext Source Files',
28900        autoScroll:true,
28901        fitToFrame:true
28902    },
28903    center : {
28904        el: cview,
28905        autoScroll:true,
28906        fitToFrame:true,
28907        toolbar: tb,
28908        resizeEl:'cbody'
28909    }
28910 });
28911 </code></pre>
28912      * @param {Object} regions An object containing ContentPanel configs by region name
28913      */
28914     batchAdd : function(regions){
28915         this.beginUpdate();
28916         for(var rname in regions){
28917             var lr = this.regions[rname];
28918             if(lr){
28919                 this.addTypedPanels(lr, regions[rname]);
28920             }
28921         }
28922         this.endUpdate();
28923     },
28924
28925     // private
28926     addTypedPanels : function(lr, ps){
28927         if(typeof ps == 'string'){
28928             lr.add(new Roo.ContentPanel(ps));
28929         }
28930         else if(ps instanceof Array){
28931             for(var i =0, len = ps.length; i < len; i++){
28932                 this.addTypedPanels(lr, ps[i]);
28933             }
28934         }
28935         else if(!ps.events){ // raw config?
28936             var el = ps.el;
28937             delete ps.el; // prevent conflict
28938             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
28939         }
28940         else {  // panel object assumed!
28941             lr.add(ps);
28942         }
28943     },
28944     /**
28945      * Adds a xtype elements to the layout.
28946      * <pre><code>
28947
28948 layout.addxtype({
28949        xtype : 'ContentPanel',
28950        region: 'west',
28951        items: [ .... ]
28952    }
28953 );
28954
28955 layout.addxtype({
28956         xtype : 'NestedLayoutPanel',
28957         region: 'west',
28958         layout: {
28959            center: { },
28960            west: { }   
28961         },
28962         items : [ ... list of content panels or nested layout panels.. ]
28963    }
28964 );
28965 </code></pre>
28966      * @param {Object} cfg Xtype definition of item to add.
28967      */
28968     addxtype : function(cfg)
28969     {
28970         // basically accepts a pannel...
28971         // can accept a layout region..!?!?
28972         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
28973         
28974         if (!cfg.xtype.match(/Panel$/)) {
28975             return false;
28976         }
28977         var ret = false;
28978         
28979         if (typeof(cfg.region) == 'undefined') {
28980             Roo.log("Failed to add Panel, region was not set");
28981             Roo.log(cfg);
28982             return false;
28983         }
28984         var region = cfg.region;
28985         delete cfg.region;
28986         
28987           
28988         var xitems = [];
28989         if (cfg.items) {
28990             xitems = cfg.items;
28991             delete cfg.items;
28992         }
28993         var nb = false;
28994         
28995         switch(cfg.xtype) 
28996         {
28997             case 'ContentPanel':  // ContentPanel (el, cfg)
28998             case 'ScrollPanel':  // ContentPanel (el, cfg)
28999             case 'ViewPanel': 
29000                 if(cfg.autoCreate) {
29001                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29002                 } else {
29003                     var el = this.el.createChild();
29004                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29005                 }
29006                 
29007                 this.add(region, ret);
29008                 break;
29009             
29010             
29011             case 'TreePanel': // our new panel!
29012                 cfg.el = this.el.createChild();
29013                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29014                 this.add(region, ret);
29015                 break;
29016             
29017             case 'NestedLayoutPanel': 
29018                 // create a new Layout (which is  a Border Layout...
29019                 var el = this.el.createChild();
29020                 var clayout = cfg.layout;
29021                 delete cfg.layout;
29022                 clayout.items   = clayout.items  || [];
29023                 // replace this exitems with the clayout ones..
29024                 xitems = clayout.items;
29025                  
29026                 
29027                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29028                     cfg.background = false;
29029                 }
29030                 var layout = new Roo.BorderLayout(el, clayout);
29031                 
29032                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29033                 //console.log('adding nested layout panel '  + cfg.toSource());
29034                 this.add(region, ret);
29035                 nb = {}; /// find first...
29036                 break;
29037                 
29038             case 'GridPanel': 
29039             
29040                 // needs grid and region
29041                 
29042                 //var el = this.getRegion(region).el.createChild();
29043                 var el = this.el.createChild();
29044                 // create the grid first...
29045                 
29046                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29047                 delete cfg.grid;
29048                 if (region == 'center' && this.active ) {
29049                     cfg.background = false;
29050                 }
29051                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29052                 
29053                 this.add(region, ret);
29054                 if (cfg.background) {
29055                     ret.on('activate', function(gp) {
29056                         if (!gp.grid.rendered) {
29057                             gp.grid.render();
29058                         }
29059                     });
29060                 } else {
29061                     grid.render();
29062                 }
29063                 break;
29064            
29065            
29066            
29067                 
29068                 
29069                 
29070             default:
29071                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29072                     
29073                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29074                     this.add(region, ret);
29075                 } else {
29076                 
29077                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29078                     return null;
29079                 }
29080                 
29081              // GridPanel (grid, cfg)
29082             
29083         }
29084         this.beginUpdate();
29085         // add children..
29086         var region = '';
29087         var abn = {};
29088         Roo.each(xitems, function(i)  {
29089             region = nb && i.region ? i.region : false;
29090             
29091             var add = ret.addxtype(i);
29092            
29093             if (region) {
29094                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29095                 if (!i.background) {
29096                     abn[region] = nb[region] ;
29097                 }
29098             }
29099             
29100         });
29101         this.endUpdate();
29102
29103         // make the last non-background panel active..
29104         //if (nb) { Roo.log(abn); }
29105         if (nb) {
29106             
29107             for(var r in abn) {
29108                 region = this.getRegion(r);
29109                 if (region) {
29110                     // tried using nb[r], but it does not work..
29111                      
29112                     region.showPanel(abn[r]);
29113                    
29114                 }
29115             }
29116         }
29117         return ret;
29118         
29119     }
29120 });
29121
29122 /**
29123  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29124  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29125  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29126  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29127  * <pre><code>
29128 // shorthand
29129 var CP = Roo.ContentPanel;
29130
29131 var layout = Roo.BorderLayout.create({
29132     north: {
29133         initialSize: 25,
29134         titlebar: false,
29135         panels: [new CP("north", "North")]
29136     },
29137     west: {
29138         split:true,
29139         initialSize: 200,
29140         minSize: 175,
29141         maxSize: 400,
29142         titlebar: true,
29143         collapsible: true,
29144         panels: [new CP("west", {title: "West"})]
29145     },
29146     east: {
29147         split:true,
29148         initialSize: 202,
29149         minSize: 175,
29150         maxSize: 400,
29151         titlebar: true,
29152         collapsible: true,
29153         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29154     },
29155     south: {
29156         split:true,
29157         initialSize: 100,
29158         minSize: 100,
29159         maxSize: 200,
29160         titlebar: true,
29161         collapsible: true,
29162         panels: [new CP("south", {title: "South", closable: true})]
29163     },
29164     center: {
29165         titlebar: true,
29166         autoScroll:true,
29167         resizeTabs: true,
29168         minTabWidth: 50,
29169         preferredTabWidth: 150,
29170         panels: [
29171             new CP("center1", {title: "Close Me", closable: true}),
29172             new CP("center2", {title: "Center Panel", closable: false})
29173         ]
29174     }
29175 }, document.body);
29176
29177 layout.getRegion("center").showPanel("center1");
29178 </code></pre>
29179  * @param config
29180  * @param targetEl
29181  */
29182 Roo.BorderLayout.create = function(config, targetEl){
29183     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29184     layout.beginUpdate();
29185     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29186     for(var j = 0, jlen = regions.length; j < jlen; j++){
29187         var lr = regions[j];
29188         if(layout.regions[lr] && config[lr].panels){
29189             var r = layout.regions[lr];
29190             var ps = config[lr].panels;
29191             layout.addTypedPanels(r, ps);
29192         }
29193     }
29194     layout.endUpdate();
29195     return layout;
29196 };
29197
29198 // private
29199 Roo.BorderLayout.RegionFactory = {
29200     // private
29201     validRegions : ["north","south","east","west","center"],
29202
29203     // private
29204     create : function(target, mgr, config){
29205         target = target.toLowerCase();
29206         if(config.lightweight || config.basic){
29207             return new Roo.BasicLayoutRegion(mgr, config, target);
29208         }
29209         switch(target){
29210             case "north":
29211                 return new Roo.NorthLayoutRegion(mgr, config);
29212             case "south":
29213                 return new Roo.SouthLayoutRegion(mgr, config);
29214             case "east":
29215                 return new Roo.EastLayoutRegion(mgr, config);
29216             case "west":
29217                 return new Roo.WestLayoutRegion(mgr, config);
29218             case "center":
29219                 return new Roo.CenterLayoutRegion(mgr, config);
29220         }
29221         throw 'Layout region "'+target+'" not supported.';
29222     }
29223 };/*
29224  * Based on:
29225  * Ext JS Library 1.1.1
29226  * Copyright(c) 2006-2007, Ext JS, LLC.
29227  *
29228  * Originally Released Under LGPL - original licence link has changed is not relivant.
29229  *
29230  * Fork - LGPL
29231  * <script type="text/javascript">
29232  */
29233  
29234 /**
29235  * @class Roo.BasicLayoutRegion
29236  * @extends Roo.util.Observable
29237  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29238  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29239  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29240  */
29241 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29242     this.mgr = mgr;
29243     this.position  = pos;
29244     this.events = {
29245         /**
29246          * @scope Roo.BasicLayoutRegion
29247          */
29248         
29249         /**
29250          * @event beforeremove
29251          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29252          * @param {Roo.LayoutRegion} this
29253          * @param {Roo.ContentPanel} panel The panel
29254          * @param {Object} e The cancel event object
29255          */
29256         "beforeremove" : true,
29257         /**
29258          * @event invalidated
29259          * Fires when the layout for this region is changed.
29260          * @param {Roo.LayoutRegion} this
29261          */
29262         "invalidated" : true,
29263         /**
29264          * @event visibilitychange
29265          * Fires when this region is shown or hidden 
29266          * @param {Roo.LayoutRegion} this
29267          * @param {Boolean} visibility true or false
29268          */
29269         "visibilitychange" : true,
29270         /**
29271          * @event paneladded
29272          * Fires when a panel is added. 
29273          * @param {Roo.LayoutRegion} this
29274          * @param {Roo.ContentPanel} panel The panel
29275          */
29276         "paneladded" : true,
29277         /**
29278          * @event panelremoved
29279          * Fires when a panel is removed. 
29280          * @param {Roo.LayoutRegion} this
29281          * @param {Roo.ContentPanel} panel The panel
29282          */
29283         "panelremoved" : true,
29284         /**
29285          * @event beforecollapse
29286          * Fires when this region before collapse.
29287          * @param {Roo.LayoutRegion} this
29288          */
29289         "beforecollapse" : true,
29290         /**
29291          * @event collapsed
29292          * Fires when this region is collapsed.
29293          * @param {Roo.LayoutRegion} this
29294          */
29295         "collapsed" : true,
29296         /**
29297          * @event expanded
29298          * Fires when this region is expanded.
29299          * @param {Roo.LayoutRegion} this
29300          */
29301         "expanded" : true,
29302         /**
29303          * @event slideshow
29304          * Fires when this region is slid into view.
29305          * @param {Roo.LayoutRegion} this
29306          */
29307         "slideshow" : true,
29308         /**
29309          * @event slidehide
29310          * Fires when this region slides out of view. 
29311          * @param {Roo.LayoutRegion} this
29312          */
29313         "slidehide" : true,
29314         /**
29315          * @event panelactivated
29316          * Fires when a panel is activated. 
29317          * @param {Roo.LayoutRegion} this
29318          * @param {Roo.ContentPanel} panel The activated panel
29319          */
29320         "panelactivated" : true,
29321         /**
29322          * @event resized
29323          * Fires when the user resizes this region. 
29324          * @param {Roo.LayoutRegion} this
29325          * @param {Number} newSize The new size (width for east/west, height for north/south)
29326          */
29327         "resized" : true
29328     };
29329     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29330     this.panels = new Roo.util.MixedCollection();
29331     this.panels.getKey = this.getPanelId.createDelegate(this);
29332     this.box = null;
29333     this.activePanel = null;
29334     // ensure listeners are added...
29335     
29336     if (config.listeners || config.events) {
29337         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29338             listeners : config.listeners || {},
29339             events : config.events || {}
29340         });
29341     }
29342     
29343     if(skipConfig !== true){
29344         this.applyConfig(config);
29345     }
29346 };
29347
29348 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29349     getPanelId : function(p){
29350         return p.getId();
29351     },
29352     
29353     applyConfig : function(config){
29354         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29355         this.config = config;
29356         
29357     },
29358     
29359     /**
29360      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29361      * the width, for horizontal (north, south) the height.
29362      * @param {Number} newSize The new width or height
29363      */
29364     resizeTo : function(newSize){
29365         var el = this.el ? this.el :
29366                  (this.activePanel ? this.activePanel.getEl() : null);
29367         if(el){
29368             switch(this.position){
29369                 case "east":
29370                 case "west":
29371                     el.setWidth(newSize);
29372                     this.fireEvent("resized", this, newSize);
29373                 break;
29374                 case "north":
29375                 case "south":
29376                     el.setHeight(newSize);
29377                     this.fireEvent("resized", this, newSize);
29378                 break;                
29379             }
29380         }
29381     },
29382     
29383     getBox : function(){
29384         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29385     },
29386     
29387     getMargins : function(){
29388         return this.margins;
29389     },
29390     
29391     updateBox : function(box){
29392         this.box = box;
29393         var el = this.activePanel.getEl();
29394         el.dom.style.left = box.x + "px";
29395         el.dom.style.top = box.y + "px";
29396         this.activePanel.setSize(box.width, box.height);
29397     },
29398     
29399     /**
29400      * Returns the container element for this region.
29401      * @return {Roo.Element}
29402      */
29403     getEl : function(){
29404         return this.activePanel;
29405     },
29406     
29407     /**
29408      * Returns true if this region is currently visible.
29409      * @return {Boolean}
29410      */
29411     isVisible : function(){
29412         return this.activePanel ? true : false;
29413     },
29414     
29415     setActivePanel : function(panel){
29416         panel = this.getPanel(panel);
29417         if(this.activePanel && this.activePanel != panel){
29418             this.activePanel.setActiveState(false);
29419             this.activePanel.getEl().setLeftTop(-10000,-10000);
29420         }
29421         this.activePanel = panel;
29422         panel.setActiveState(true);
29423         if(this.box){
29424             panel.setSize(this.box.width, this.box.height);
29425         }
29426         this.fireEvent("panelactivated", this, panel);
29427         this.fireEvent("invalidated");
29428     },
29429     
29430     /**
29431      * Show the specified panel.
29432      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29433      * @return {Roo.ContentPanel} The shown panel or null
29434      */
29435     showPanel : function(panel){
29436         if(panel = this.getPanel(panel)){
29437             this.setActivePanel(panel);
29438         }
29439         return panel;
29440     },
29441     
29442     /**
29443      * Get the active panel for this region.
29444      * @return {Roo.ContentPanel} The active panel or null
29445      */
29446     getActivePanel : function(){
29447         return this.activePanel;
29448     },
29449     
29450     /**
29451      * Add the passed ContentPanel(s)
29452      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29453      * @return {Roo.ContentPanel} The panel added (if only one was added)
29454      */
29455     add : function(panel){
29456         if(arguments.length > 1){
29457             for(var i = 0, len = arguments.length; i < len; i++) {
29458                 this.add(arguments[i]);
29459             }
29460             return null;
29461         }
29462         if(this.hasPanel(panel)){
29463             this.showPanel(panel);
29464             return panel;
29465         }
29466         var el = panel.getEl();
29467         if(el.dom.parentNode != this.mgr.el.dom){
29468             this.mgr.el.dom.appendChild(el.dom);
29469         }
29470         if(panel.setRegion){
29471             panel.setRegion(this);
29472         }
29473         this.panels.add(panel);
29474         el.setStyle("position", "absolute");
29475         if(!panel.background){
29476             this.setActivePanel(panel);
29477             if(this.config.initialSize && this.panels.getCount()==1){
29478                 this.resizeTo(this.config.initialSize);
29479             }
29480         }
29481         this.fireEvent("paneladded", this, panel);
29482         return panel;
29483     },
29484     
29485     /**
29486      * Returns true if the panel is in this region.
29487      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29488      * @return {Boolean}
29489      */
29490     hasPanel : function(panel){
29491         if(typeof panel == "object"){ // must be panel obj
29492             panel = panel.getId();
29493         }
29494         return this.getPanel(panel) ? true : false;
29495     },
29496     
29497     /**
29498      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29499      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29500      * @param {Boolean} preservePanel Overrides the config preservePanel option
29501      * @return {Roo.ContentPanel} The panel that was removed
29502      */
29503     remove : function(panel, preservePanel){
29504         panel = this.getPanel(panel);
29505         if(!panel){
29506             return null;
29507         }
29508         var e = {};
29509         this.fireEvent("beforeremove", this, panel, e);
29510         if(e.cancel === true){
29511             return null;
29512         }
29513         var panelId = panel.getId();
29514         this.panels.removeKey(panelId);
29515         return panel;
29516     },
29517     
29518     /**
29519      * Returns the panel specified or null if it's not in this region.
29520      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29521      * @return {Roo.ContentPanel}
29522      */
29523     getPanel : function(id){
29524         if(typeof id == "object"){ // must be panel obj
29525             return id;
29526         }
29527         return this.panels.get(id);
29528     },
29529     
29530     /**
29531      * Returns this regions position (north/south/east/west/center).
29532      * @return {String} 
29533      */
29534     getPosition: function(){
29535         return this.position;    
29536     }
29537 });/*
29538  * Based on:
29539  * Ext JS Library 1.1.1
29540  * Copyright(c) 2006-2007, Ext JS, LLC.
29541  *
29542  * Originally Released Under LGPL - original licence link has changed is not relivant.
29543  *
29544  * Fork - LGPL
29545  * <script type="text/javascript">
29546  */
29547  
29548 /**
29549  * @class Roo.LayoutRegion
29550  * @extends Roo.BasicLayoutRegion
29551  * This class represents a region in a layout manager.
29552  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
29553  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
29554  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
29555  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29556  * @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})
29557  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
29558  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
29559  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
29560  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
29561  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
29562  * @cfg {String}    title           The title for the region (overrides panel titles)
29563  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
29564  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29565  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
29566  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29567  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
29568  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29569  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
29570  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
29571  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
29572  * @cfg {Boolean}   showPin         True to show a pin button
29573  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
29574  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
29575  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
29576  * @cfg {Number}    width           For East/West panels
29577  * @cfg {Number}    height          For North/South panels
29578  * @cfg {Boolean}   split           To show the splitter
29579  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
29580  */
29581 Roo.LayoutRegion = function(mgr, config, pos){
29582     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29583     var dh = Roo.DomHelper;
29584     /** This region's container element 
29585     * @type Roo.Element */
29586     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29587     /** This region's title element 
29588     * @type Roo.Element */
29589
29590     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29591         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
29592         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29593     ]}, true);
29594     this.titleEl.enableDisplayMode();
29595     /** This region's title text element 
29596     * @type HTMLElement */
29597     this.titleTextEl = this.titleEl.dom.firstChild;
29598     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
29599     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
29600     this.closeBtn.enableDisplayMode();
29601     this.closeBtn.on("click", this.closeClicked, this);
29602     this.closeBtn.hide();
29603
29604     this.createBody(config);
29605     this.visible = true;
29606     this.collapsed = false;
29607
29608     if(config.hideWhenEmpty){
29609         this.hide();
29610         this.on("paneladded", this.validateVisibility, this);
29611         this.on("panelremoved", this.validateVisibility, this);
29612     }
29613     this.applyConfig(config);
29614 };
29615
29616 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
29617
29618     createBody : function(){
29619         /** This region's body element 
29620         * @type Roo.Element */
29621         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
29622     },
29623
29624     applyConfig : function(c){
29625         if(c.collapsible && this.position != "center" && !this.collapsedEl){
29626             var dh = Roo.DomHelper;
29627             if(c.titlebar !== false){
29628                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
29629                 this.collapseBtn.on("click", this.collapse, this);
29630                 this.collapseBtn.enableDisplayMode();
29631
29632                 if(c.showPin === true || this.showPin){
29633                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
29634                     this.stickBtn.enableDisplayMode();
29635                     this.stickBtn.on("click", this.expand, this);
29636                     this.stickBtn.hide();
29637                 }
29638             }
29639             /** This region's collapsed element
29640             * @type Roo.Element */
29641             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
29642                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
29643             ]}, true);
29644             if(c.floatable !== false){
29645                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
29646                this.collapsedEl.on("click", this.collapseClick, this);
29647             }
29648
29649             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
29650                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
29651                    id: "message", unselectable: "on", style:{"float":"left"}});
29652                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
29653              }
29654             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
29655             this.expandBtn.on("click", this.expand, this);
29656         }
29657         if(this.collapseBtn){
29658             this.collapseBtn.setVisible(c.collapsible == true);
29659         }
29660         this.cmargins = c.cmargins || this.cmargins ||
29661                          (this.position == "west" || this.position == "east" ?
29662                              {top: 0, left: 2, right:2, bottom: 0} :
29663                              {top: 2, left: 0, right:0, bottom: 2});
29664         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29665         this.bottomTabs = c.tabPosition != "top";
29666         this.autoScroll = c.autoScroll || false;
29667         if(this.autoScroll){
29668             this.bodyEl.setStyle("overflow", "auto");
29669         }else{
29670             this.bodyEl.setStyle("overflow", "hidden");
29671         }
29672         //if(c.titlebar !== false){
29673             if((!c.titlebar && !c.title) || c.titlebar === false){
29674                 this.titleEl.hide();
29675             }else{
29676                 this.titleEl.show();
29677                 if(c.title){
29678                     this.titleTextEl.innerHTML = c.title;
29679                 }
29680             }
29681         //}
29682         this.duration = c.duration || .30;
29683         this.slideDuration = c.slideDuration || .45;
29684         this.config = c;
29685         if(c.collapsed){
29686             this.collapse(true);
29687         }
29688         if(c.hidden){
29689             this.hide();
29690         }
29691     },
29692     /**
29693      * Returns true if this region is currently visible.
29694      * @return {Boolean}
29695      */
29696     isVisible : function(){
29697         return this.visible;
29698     },
29699
29700     /**
29701      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
29702      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
29703      */
29704     setCollapsedTitle : function(title){
29705         title = title || "&#160;";
29706         if(this.collapsedTitleTextEl){
29707             this.collapsedTitleTextEl.innerHTML = title;
29708         }
29709     },
29710
29711     getBox : function(){
29712         var b;
29713         if(!this.collapsed){
29714             b = this.el.getBox(false, true);
29715         }else{
29716             b = this.collapsedEl.getBox(false, true);
29717         }
29718         return b;
29719     },
29720
29721     getMargins : function(){
29722         return this.collapsed ? this.cmargins : this.margins;
29723     },
29724
29725     highlight : function(){
29726         this.el.addClass("x-layout-panel-dragover");
29727     },
29728
29729     unhighlight : function(){
29730         this.el.removeClass("x-layout-panel-dragover");
29731     },
29732
29733     updateBox : function(box){
29734         this.box = box;
29735         if(!this.collapsed){
29736             this.el.dom.style.left = box.x + "px";
29737             this.el.dom.style.top = box.y + "px";
29738             this.updateBody(box.width, box.height);
29739         }else{
29740             this.collapsedEl.dom.style.left = box.x + "px";
29741             this.collapsedEl.dom.style.top = box.y + "px";
29742             this.collapsedEl.setSize(box.width, box.height);
29743         }
29744         if(this.tabs){
29745             this.tabs.autoSizeTabs();
29746         }
29747     },
29748
29749     updateBody : function(w, h){
29750         if(w !== null){
29751             this.el.setWidth(w);
29752             w -= this.el.getBorderWidth("rl");
29753             if(this.config.adjustments){
29754                 w += this.config.adjustments[0];
29755             }
29756         }
29757         if(h !== null){
29758             this.el.setHeight(h);
29759             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
29760             h -= this.el.getBorderWidth("tb");
29761             if(this.config.adjustments){
29762                 h += this.config.adjustments[1];
29763             }
29764             this.bodyEl.setHeight(h);
29765             if(this.tabs){
29766                 h = this.tabs.syncHeight(h);
29767             }
29768         }
29769         if(this.panelSize){
29770             w = w !== null ? w : this.panelSize.width;
29771             h = h !== null ? h : this.panelSize.height;
29772         }
29773         if(this.activePanel){
29774             var el = this.activePanel.getEl();
29775             w = w !== null ? w : el.getWidth();
29776             h = h !== null ? h : el.getHeight();
29777             this.panelSize = {width: w, height: h};
29778             this.activePanel.setSize(w, h);
29779         }
29780         if(Roo.isIE && this.tabs){
29781             this.tabs.el.repaint();
29782         }
29783     },
29784
29785     /**
29786      * Returns the container element for this region.
29787      * @return {Roo.Element}
29788      */
29789     getEl : function(){
29790         return this.el;
29791     },
29792
29793     /**
29794      * Hides this region.
29795      */
29796     hide : function(){
29797         if(!this.collapsed){
29798             this.el.dom.style.left = "-2000px";
29799             this.el.hide();
29800         }else{
29801             this.collapsedEl.dom.style.left = "-2000px";
29802             this.collapsedEl.hide();
29803         }
29804         this.visible = false;
29805         this.fireEvent("visibilitychange", this, false);
29806     },
29807
29808     /**
29809      * Shows this region if it was previously hidden.
29810      */
29811     show : function(){
29812         if(!this.collapsed){
29813             this.el.show();
29814         }else{
29815             this.collapsedEl.show();
29816         }
29817         this.visible = true;
29818         this.fireEvent("visibilitychange", this, true);
29819     },
29820
29821     closeClicked : function(){
29822         if(this.activePanel){
29823             this.remove(this.activePanel);
29824         }
29825     },
29826
29827     collapseClick : function(e){
29828         if(this.isSlid){
29829            e.stopPropagation();
29830            this.slideIn();
29831         }else{
29832            e.stopPropagation();
29833            this.slideOut();
29834         }
29835     },
29836
29837     /**
29838      * Collapses this region.
29839      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
29840      */
29841     collapse : function(skipAnim, skipCheck = false){
29842         if(this.collapsed) {
29843             return;
29844         }
29845         
29846         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
29847             
29848             this.collapsed = true;
29849             if(this.split){
29850                 this.split.el.hide();
29851             }
29852             if(this.config.animate && skipAnim !== true){
29853                 this.fireEvent("invalidated", this);
29854                 this.animateCollapse();
29855             }else{
29856                 this.el.setLocation(-20000,-20000);
29857                 this.el.hide();
29858                 this.collapsedEl.show();
29859                 this.fireEvent("collapsed", this);
29860                 this.fireEvent("invalidated", this);
29861             }
29862         }
29863         
29864     },
29865
29866     animateCollapse : function(){
29867         // overridden
29868     },
29869
29870     /**
29871      * Expands this region if it was previously collapsed.
29872      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
29873      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
29874      */
29875     expand : function(e, skipAnim){
29876         if(e) {
29877             e.stopPropagation();
29878         }
29879         if(!this.collapsed || this.el.hasActiveFx()) {
29880             return;
29881         }
29882         if(this.isSlid){
29883             this.afterSlideIn();
29884             skipAnim = true;
29885         }
29886         this.collapsed = false;
29887         if(this.config.animate && skipAnim !== true){
29888             this.animateExpand();
29889         }else{
29890             this.el.show();
29891             if(this.split){
29892                 this.split.el.show();
29893             }
29894             this.collapsedEl.setLocation(-2000,-2000);
29895             this.collapsedEl.hide();
29896             this.fireEvent("invalidated", this);
29897             this.fireEvent("expanded", this);
29898         }
29899     },
29900
29901     animateExpand : function(){
29902         // overridden
29903     },
29904
29905     initTabs : function()
29906     {
29907         this.bodyEl.setStyle("overflow", "hidden");
29908         var ts = new Roo.TabPanel(
29909                 this.bodyEl.dom,
29910                 {
29911                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
29912                     disableTooltips: this.config.disableTabTips,
29913                     toolbar : this.config.toolbar
29914                 }
29915         );
29916         if(this.config.hideTabs){
29917             ts.stripWrap.setDisplayed(false);
29918         }
29919         this.tabs = ts;
29920         ts.resizeTabs = this.config.resizeTabs === true;
29921         ts.minTabWidth = this.config.minTabWidth || 40;
29922         ts.maxTabWidth = this.config.maxTabWidth || 250;
29923         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
29924         ts.monitorResize = false;
29925         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
29926         ts.bodyEl.addClass('x-layout-tabs-body');
29927         this.panels.each(this.initPanelAsTab, this);
29928     },
29929
29930     initPanelAsTab : function(panel){
29931         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
29932                     this.config.closeOnTab && panel.isClosable());
29933         if(panel.tabTip !== undefined){
29934             ti.setTooltip(panel.tabTip);
29935         }
29936         ti.on("activate", function(){
29937               this.setActivePanel(panel);
29938         }, this);
29939         if(this.config.closeOnTab){
29940             ti.on("beforeclose", function(t, e){
29941                 e.cancel = true;
29942                 this.remove(panel);
29943             }, this);
29944         }
29945         return ti;
29946     },
29947
29948     updatePanelTitle : function(panel, title){
29949         if(this.activePanel == panel){
29950             this.updateTitle(title);
29951         }
29952         if(this.tabs){
29953             var ti = this.tabs.getTab(panel.getEl().id);
29954             ti.setText(title);
29955             if(panel.tabTip !== undefined){
29956                 ti.setTooltip(panel.tabTip);
29957             }
29958         }
29959     },
29960
29961     updateTitle : function(title){
29962         if(this.titleTextEl && !this.config.title){
29963             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
29964         }
29965     },
29966
29967     setActivePanel : function(panel){
29968         panel = this.getPanel(panel);
29969         if(this.activePanel && this.activePanel != panel){
29970             this.activePanel.setActiveState(false);
29971         }
29972         this.activePanel = panel;
29973         panel.setActiveState(true);
29974         if(this.panelSize){
29975             panel.setSize(this.panelSize.width, this.panelSize.height);
29976         }
29977         if(this.closeBtn){
29978             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
29979         }
29980         this.updateTitle(panel.getTitle());
29981         if(this.tabs){
29982             this.fireEvent("invalidated", this);
29983         }
29984         this.fireEvent("panelactivated", this, panel);
29985     },
29986
29987     /**
29988      * Shows the specified panel.
29989      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
29990      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
29991      */
29992     showPanel : function(panel)
29993     {
29994         panel = this.getPanel(panel);
29995         if(panel){
29996             if(this.tabs){
29997                 var tab = this.tabs.getTab(panel.getEl().id);
29998                 if(tab.isHidden()){
29999                     this.tabs.unhideTab(tab.id);
30000                 }
30001                 tab.activate();
30002             }else{
30003                 this.setActivePanel(panel);
30004             }
30005         }
30006         return panel;
30007     },
30008
30009     /**
30010      * Get the active panel for this region.
30011      * @return {Roo.ContentPanel} The active panel or null
30012      */
30013     getActivePanel : function(){
30014         return this.activePanel;
30015     },
30016
30017     validateVisibility : function(){
30018         if(this.panels.getCount() < 1){
30019             this.updateTitle("&#160;");
30020             this.closeBtn.hide();
30021             this.hide();
30022         }else{
30023             if(!this.isVisible()){
30024                 this.show();
30025             }
30026         }
30027     },
30028
30029     /**
30030      * Adds the passed ContentPanel(s) to this region.
30031      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30032      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30033      */
30034     add : function(panel){
30035         if(arguments.length > 1){
30036             for(var i = 0, len = arguments.length; i < len; i++) {
30037                 this.add(arguments[i]);
30038             }
30039             return null;
30040         }
30041         if(this.hasPanel(panel)){
30042             this.showPanel(panel);
30043             return panel;
30044         }
30045         panel.setRegion(this);
30046         this.panels.add(panel);
30047         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30048             this.bodyEl.dom.appendChild(panel.getEl().dom);
30049             if(panel.background !== true){
30050                 this.setActivePanel(panel);
30051             }
30052             this.fireEvent("paneladded", this, panel);
30053             return panel;
30054         }
30055         if(!this.tabs){
30056             this.initTabs();
30057         }else{
30058             this.initPanelAsTab(panel);
30059         }
30060         if(panel.background !== true){
30061             this.tabs.activate(panel.getEl().id);
30062         }
30063         this.fireEvent("paneladded", this, panel);
30064         return panel;
30065     },
30066
30067     /**
30068      * Hides the tab for the specified panel.
30069      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30070      */
30071     hidePanel : function(panel){
30072         if(this.tabs && (panel = this.getPanel(panel))){
30073             this.tabs.hideTab(panel.getEl().id);
30074         }
30075     },
30076
30077     /**
30078      * Unhides the tab for a previously hidden panel.
30079      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30080      */
30081     unhidePanel : function(panel){
30082         if(this.tabs && (panel = this.getPanel(panel))){
30083             this.tabs.unhideTab(panel.getEl().id);
30084         }
30085     },
30086
30087     clearPanels : function(){
30088         while(this.panels.getCount() > 0){
30089              this.remove(this.panels.first());
30090         }
30091     },
30092
30093     /**
30094      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30095      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30096      * @param {Boolean} preservePanel Overrides the config preservePanel option
30097      * @return {Roo.ContentPanel} The panel that was removed
30098      */
30099     remove : function(panel, preservePanel){
30100         panel = this.getPanel(panel);
30101         if(!panel){
30102             return null;
30103         }
30104         var e = {};
30105         this.fireEvent("beforeremove", this, panel, e);
30106         if(e.cancel === true){
30107             return null;
30108         }
30109         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30110         var panelId = panel.getId();
30111         this.panels.removeKey(panelId);
30112         if(preservePanel){
30113             document.body.appendChild(panel.getEl().dom);
30114         }
30115         if(this.tabs){
30116             this.tabs.removeTab(panel.getEl().id);
30117         }else if (!preservePanel){
30118             this.bodyEl.dom.removeChild(panel.getEl().dom);
30119         }
30120         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30121             var p = this.panels.first();
30122             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30123             tempEl.appendChild(p.getEl().dom);
30124             this.bodyEl.update("");
30125             this.bodyEl.dom.appendChild(p.getEl().dom);
30126             tempEl = null;
30127             this.updateTitle(p.getTitle());
30128             this.tabs = null;
30129             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30130             this.setActivePanel(p);
30131         }
30132         panel.setRegion(null);
30133         if(this.activePanel == panel){
30134             this.activePanel = null;
30135         }
30136         if(this.config.autoDestroy !== false && preservePanel !== true){
30137             try{panel.destroy();}catch(e){}
30138         }
30139         this.fireEvent("panelremoved", this, panel);
30140         return panel;
30141     },
30142
30143     /**
30144      * Returns the TabPanel component used by this region
30145      * @return {Roo.TabPanel}
30146      */
30147     getTabs : function(){
30148         return this.tabs;
30149     },
30150
30151     createTool : function(parentEl, className){
30152         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30153             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30154         btn.addClassOnOver("x-layout-tools-button-over");
30155         return btn;
30156     }
30157 });/*
30158  * Based on:
30159  * Ext JS Library 1.1.1
30160  * Copyright(c) 2006-2007, Ext JS, LLC.
30161  *
30162  * Originally Released Under LGPL - original licence link has changed is not relivant.
30163  *
30164  * Fork - LGPL
30165  * <script type="text/javascript">
30166  */
30167  
30168
30169
30170 /**
30171  * @class Roo.SplitLayoutRegion
30172  * @extends Roo.LayoutRegion
30173  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30174  */
30175 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30176     this.cursor = cursor;
30177     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30178 };
30179
30180 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30181     splitTip : "Drag to resize.",
30182     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30183     useSplitTips : false,
30184
30185     applyConfig : function(config){
30186         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30187         if(config.split){
30188             if(!this.split){
30189                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30190                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30191                 /** The SplitBar for this region 
30192                 * @type Roo.SplitBar */
30193                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30194                 this.split.on("moved", this.onSplitMove, this);
30195                 this.split.useShim = config.useShim === true;
30196                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30197                 if(this.useSplitTips){
30198                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30199                 }
30200                 if(config.collapsible){
30201                     this.split.el.on("dblclick", this.collapse,  this);
30202                 }
30203             }
30204             if(typeof config.minSize != "undefined"){
30205                 this.split.minSize = config.minSize;
30206             }
30207             if(typeof config.maxSize != "undefined"){
30208                 this.split.maxSize = config.maxSize;
30209             }
30210             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30211                 this.hideSplitter();
30212             }
30213         }
30214     },
30215
30216     getHMaxSize : function(){
30217          var cmax = this.config.maxSize || 10000;
30218          var center = this.mgr.getRegion("center");
30219          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30220     },
30221
30222     getVMaxSize : function(){
30223          var cmax = this.config.maxSize || 10000;
30224          var center = this.mgr.getRegion("center");
30225          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30226     },
30227
30228     onSplitMove : function(split, newSize){
30229         this.fireEvent("resized", this, newSize);
30230     },
30231     
30232     /** 
30233      * Returns the {@link Roo.SplitBar} for this region.
30234      * @return {Roo.SplitBar}
30235      */
30236     getSplitBar : function(){
30237         return this.split;
30238     },
30239     
30240     hide : function(){
30241         this.hideSplitter();
30242         Roo.SplitLayoutRegion.superclass.hide.call(this);
30243     },
30244
30245     hideSplitter : function(){
30246         if(this.split){
30247             this.split.el.setLocation(-2000,-2000);
30248             this.split.el.hide();
30249         }
30250     },
30251
30252     show : function(){
30253         if(this.split){
30254             this.split.el.show();
30255         }
30256         Roo.SplitLayoutRegion.superclass.show.call(this);
30257     },
30258     
30259     beforeSlide: function(){
30260         if(Roo.isGecko){// firefox overflow auto bug workaround
30261             this.bodyEl.clip();
30262             if(this.tabs) {
30263                 this.tabs.bodyEl.clip();
30264             }
30265             if(this.activePanel){
30266                 this.activePanel.getEl().clip();
30267                 
30268                 if(this.activePanel.beforeSlide){
30269                     this.activePanel.beforeSlide();
30270                 }
30271             }
30272         }
30273     },
30274     
30275     afterSlide : function(){
30276         if(Roo.isGecko){// firefox overflow auto bug workaround
30277             this.bodyEl.unclip();
30278             if(this.tabs) {
30279                 this.tabs.bodyEl.unclip();
30280             }
30281             if(this.activePanel){
30282                 this.activePanel.getEl().unclip();
30283                 if(this.activePanel.afterSlide){
30284                     this.activePanel.afterSlide();
30285                 }
30286             }
30287         }
30288     },
30289
30290     initAutoHide : function(){
30291         if(this.autoHide !== false){
30292             if(!this.autoHideHd){
30293                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30294                 this.autoHideHd = {
30295                     "mouseout": function(e){
30296                         if(!e.within(this.el, true)){
30297                             st.delay(500);
30298                         }
30299                     },
30300                     "mouseover" : function(e){
30301                         st.cancel();
30302                     },
30303                     scope : this
30304                 };
30305             }
30306             this.el.on(this.autoHideHd);
30307         }
30308     },
30309
30310     clearAutoHide : function(){
30311         if(this.autoHide !== false){
30312             this.el.un("mouseout", this.autoHideHd.mouseout);
30313             this.el.un("mouseover", this.autoHideHd.mouseover);
30314         }
30315     },
30316
30317     clearMonitor : function(){
30318         Roo.get(document).un("click", this.slideInIf, this);
30319     },
30320
30321     // these names are backwards but not changed for compat
30322     slideOut : function(){
30323         if(this.isSlid || this.el.hasActiveFx()){
30324             return;
30325         }
30326         this.isSlid = true;
30327         if(this.collapseBtn){
30328             this.collapseBtn.hide();
30329         }
30330         this.closeBtnState = this.closeBtn.getStyle('display');
30331         this.closeBtn.hide();
30332         if(this.stickBtn){
30333             this.stickBtn.show();
30334         }
30335         this.el.show();
30336         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30337         this.beforeSlide();
30338         this.el.setStyle("z-index", 10001);
30339         this.el.slideIn(this.getSlideAnchor(), {
30340             callback: function(){
30341                 this.afterSlide();
30342                 this.initAutoHide();
30343                 Roo.get(document).on("click", this.slideInIf, this);
30344                 this.fireEvent("slideshow", this);
30345             },
30346             scope: this,
30347             block: true
30348         });
30349     },
30350
30351     afterSlideIn : function(){
30352         this.clearAutoHide();
30353         this.isSlid = false;
30354         this.clearMonitor();
30355         this.el.setStyle("z-index", "");
30356         if(this.collapseBtn){
30357             this.collapseBtn.show();
30358         }
30359         this.closeBtn.setStyle('display', this.closeBtnState);
30360         if(this.stickBtn){
30361             this.stickBtn.hide();
30362         }
30363         this.fireEvent("slidehide", this);
30364     },
30365
30366     slideIn : function(cb){
30367         if(!this.isSlid || this.el.hasActiveFx()){
30368             Roo.callback(cb);
30369             return;
30370         }
30371         this.isSlid = false;
30372         this.beforeSlide();
30373         this.el.slideOut(this.getSlideAnchor(), {
30374             callback: function(){
30375                 this.el.setLeftTop(-10000, -10000);
30376                 this.afterSlide();
30377                 this.afterSlideIn();
30378                 Roo.callback(cb);
30379             },
30380             scope: this,
30381             block: true
30382         });
30383     },
30384     
30385     slideInIf : function(e){
30386         if(!e.within(this.el)){
30387             this.slideIn();
30388         }
30389     },
30390
30391     animateCollapse : function(){
30392         this.beforeSlide();
30393         this.el.setStyle("z-index", 20000);
30394         var anchor = this.getSlideAnchor();
30395         this.el.slideOut(anchor, {
30396             callback : function(){
30397                 this.el.setStyle("z-index", "");
30398                 this.collapsedEl.slideIn(anchor, {duration:.3});
30399                 this.afterSlide();
30400                 this.el.setLocation(-10000,-10000);
30401                 this.el.hide();
30402                 this.fireEvent("collapsed", this);
30403             },
30404             scope: this,
30405             block: true
30406         });
30407     },
30408
30409     animateExpand : function(){
30410         this.beforeSlide();
30411         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30412         this.el.setStyle("z-index", 20000);
30413         this.collapsedEl.hide({
30414             duration:.1
30415         });
30416         this.el.slideIn(this.getSlideAnchor(), {
30417             callback : function(){
30418                 this.el.setStyle("z-index", "");
30419                 this.afterSlide();
30420                 if(this.split){
30421                     this.split.el.show();
30422                 }
30423                 this.fireEvent("invalidated", this);
30424                 this.fireEvent("expanded", this);
30425             },
30426             scope: this,
30427             block: true
30428         });
30429     },
30430
30431     anchors : {
30432         "west" : "left",
30433         "east" : "right",
30434         "north" : "top",
30435         "south" : "bottom"
30436     },
30437
30438     sanchors : {
30439         "west" : "l",
30440         "east" : "r",
30441         "north" : "t",
30442         "south" : "b"
30443     },
30444
30445     canchors : {
30446         "west" : "tl-tr",
30447         "east" : "tr-tl",
30448         "north" : "tl-bl",
30449         "south" : "bl-tl"
30450     },
30451
30452     getAnchor : function(){
30453         return this.anchors[this.position];
30454     },
30455
30456     getCollapseAnchor : function(){
30457         return this.canchors[this.position];
30458     },
30459
30460     getSlideAnchor : function(){
30461         return this.sanchors[this.position];
30462     },
30463
30464     getAlignAdj : function(){
30465         var cm = this.cmargins;
30466         switch(this.position){
30467             case "west":
30468                 return [0, 0];
30469             break;
30470             case "east":
30471                 return [0, 0];
30472             break;
30473             case "north":
30474                 return [0, 0];
30475             break;
30476             case "south":
30477                 return [0, 0];
30478             break;
30479         }
30480     },
30481
30482     getExpandAdj : function(){
30483         var c = this.collapsedEl, cm = this.cmargins;
30484         switch(this.position){
30485             case "west":
30486                 return [-(cm.right+c.getWidth()+cm.left), 0];
30487             break;
30488             case "east":
30489                 return [cm.right+c.getWidth()+cm.left, 0];
30490             break;
30491             case "north":
30492                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30493             break;
30494             case "south":
30495                 return [0, cm.top+cm.bottom+c.getHeight()];
30496             break;
30497         }
30498     }
30499 });/*
30500  * Based on:
30501  * Ext JS Library 1.1.1
30502  * Copyright(c) 2006-2007, Ext JS, LLC.
30503  *
30504  * Originally Released Under LGPL - original licence link has changed is not relivant.
30505  *
30506  * Fork - LGPL
30507  * <script type="text/javascript">
30508  */
30509 /*
30510  * These classes are private internal classes
30511  */
30512 Roo.CenterLayoutRegion = function(mgr, config){
30513     Roo.LayoutRegion.call(this, mgr, config, "center");
30514     this.visible = true;
30515     this.minWidth = config.minWidth || 20;
30516     this.minHeight = config.minHeight || 20;
30517 };
30518
30519 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30520     hide : function(){
30521         // center panel can't be hidden
30522     },
30523     
30524     show : function(){
30525         // center panel can't be hidden
30526     },
30527     
30528     getMinWidth: function(){
30529         return this.minWidth;
30530     },
30531     
30532     getMinHeight: function(){
30533         return this.minHeight;
30534     }
30535 });
30536
30537
30538 Roo.NorthLayoutRegion = function(mgr, config){
30539     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30540     if(this.split){
30541         this.split.placement = Roo.SplitBar.TOP;
30542         this.split.orientation = Roo.SplitBar.VERTICAL;
30543         this.split.el.addClass("x-layout-split-v");
30544     }
30545     var size = config.initialSize || config.height;
30546     if(typeof size != "undefined"){
30547         this.el.setHeight(size);
30548     }
30549 };
30550 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30551     orientation: Roo.SplitBar.VERTICAL,
30552     getBox : function(){
30553         if(this.collapsed){
30554             return this.collapsedEl.getBox();
30555         }
30556         var box = this.el.getBox();
30557         if(this.split){
30558             box.height += this.split.el.getHeight();
30559         }
30560         return box;
30561     },
30562     
30563     updateBox : function(box){
30564         if(this.split && !this.collapsed){
30565             box.height -= this.split.el.getHeight();
30566             this.split.el.setLeft(box.x);
30567             this.split.el.setTop(box.y+box.height);
30568             this.split.el.setWidth(box.width);
30569         }
30570         if(this.collapsed){
30571             this.updateBody(box.width, null);
30572         }
30573         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30574     }
30575 });
30576
30577 Roo.SouthLayoutRegion = function(mgr, config){
30578     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30579     if(this.split){
30580         this.split.placement = Roo.SplitBar.BOTTOM;
30581         this.split.orientation = Roo.SplitBar.VERTICAL;
30582         this.split.el.addClass("x-layout-split-v");
30583     }
30584     var size = config.initialSize || config.height;
30585     if(typeof size != "undefined"){
30586         this.el.setHeight(size);
30587     }
30588 };
30589 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30590     orientation: Roo.SplitBar.VERTICAL,
30591     getBox : function(){
30592         if(this.collapsed){
30593             return this.collapsedEl.getBox();
30594         }
30595         var box = this.el.getBox();
30596         if(this.split){
30597             var sh = this.split.el.getHeight();
30598             box.height += sh;
30599             box.y -= sh;
30600         }
30601         return box;
30602     },
30603     
30604     updateBox : function(box){
30605         if(this.split && !this.collapsed){
30606             var sh = this.split.el.getHeight();
30607             box.height -= sh;
30608             box.y += sh;
30609             this.split.el.setLeft(box.x);
30610             this.split.el.setTop(box.y-sh);
30611             this.split.el.setWidth(box.width);
30612         }
30613         if(this.collapsed){
30614             this.updateBody(box.width, null);
30615         }
30616         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30617     }
30618 });
30619
30620 Roo.EastLayoutRegion = function(mgr, config){
30621     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
30622     if(this.split){
30623         this.split.placement = Roo.SplitBar.RIGHT;
30624         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30625         this.split.el.addClass("x-layout-split-h");
30626     }
30627     var size = config.initialSize || config.width;
30628     if(typeof size != "undefined"){
30629         this.el.setWidth(size);
30630     }
30631 };
30632 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
30633     orientation: Roo.SplitBar.HORIZONTAL,
30634     getBox : function(){
30635         if(this.collapsed){
30636             return this.collapsedEl.getBox();
30637         }
30638         var box = this.el.getBox();
30639         if(this.split){
30640             var sw = this.split.el.getWidth();
30641             box.width += sw;
30642             box.x -= sw;
30643         }
30644         return box;
30645     },
30646
30647     updateBox : function(box){
30648         if(this.split && !this.collapsed){
30649             var sw = this.split.el.getWidth();
30650             box.width -= sw;
30651             this.split.el.setLeft(box.x);
30652             this.split.el.setTop(box.y);
30653             this.split.el.setHeight(box.height);
30654             box.x += sw;
30655         }
30656         if(this.collapsed){
30657             this.updateBody(null, box.height);
30658         }
30659         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30660     }
30661 });
30662
30663 Roo.WestLayoutRegion = function(mgr, config){
30664     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
30665     if(this.split){
30666         this.split.placement = Roo.SplitBar.LEFT;
30667         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30668         this.split.el.addClass("x-layout-split-h");
30669     }
30670     var size = config.initialSize || config.width;
30671     if(typeof size != "undefined"){
30672         this.el.setWidth(size);
30673     }
30674 };
30675 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
30676     orientation: Roo.SplitBar.HORIZONTAL,
30677     getBox : function(){
30678         if(this.collapsed){
30679             return this.collapsedEl.getBox();
30680         }
30681         var box = this.el.getBox();
30682         if(this.split){
30683             box.width += this.split.el.getWidth();
30684         }
30685         return box;
30686     },
30687     
30688     updateBox : function(box){
30689         if(this.split && !this.collapsed){
30690             var sw = this.split.el.getWidth();
30691             box.width -= sw;
30692             this.split.el.setLeft(box.x+box.width);
30693             this.split.el.setTop(box.y);
30694             this.split.el.setHeight(box.height);
30695         }
30696         if(this.collapsed){
30697             this.updateBody(null, box.height);
30698         }
30699         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30700     }
30701 });
30702 /*
30703  * Based on:
30704  * Ext JS Library 1.1.1
30705  * Copyright(c) 2006-2007, Ext JS, LLC.
30706  *
30707  * Originally Released Under LGPL - original licence link has changed is not relivant.
30708  *
30709  * Fork - LGPL
30710  * <script type="text/javascript">
30711  */
30712  
30713  
30714 /*
30715  * Private internal class for reading and applying state
30716  */
30717 Roo.LayoutStateManager = function(layout){
30718      // default empty state
30719      this.state = {
30720         north: {},
30721         south: {},
30722         east: {},
30723         west: {}       
30724     };
30725 };
30726
30727 Roo.LayoutStateManager.prototype = {
30728     init : function(layout, provider){
30729         this.provider = provider;
30730         var state = provider.get(layout.id+"-layout-state");
30731         if(state){
30732             var wasUpdating = layout.isUpdating();
30733             if(!wasUpdating){
30734                 layout.beginUpdate();
30735             }
30736             for(var key in state){
30737                 if(typeof state[key] != "function"){
30738                     var rstate = state[key];
30739                     var r = layout.getRegion(key);
30740                     if(r && rstate){
30741                         if(rstate.size){
30742                             r.resizeTo(rstate.size);
30743                         }
30744                         if(rstate.collapsed == true){
30745                             r.collapse(true);
30746                         }else{
30747                             r.expand(null, true);
30748                         }
30749                     }
30750                 }
30751             }
30752             if(!wasUpdating){
30753                 layout.endUpdate();
30754             }
30755             this.state = state; 
30756         }
30757         this.layout = layout;
30758         layout.on("regionresized", this.onRegionResized, this);
30759         layout.on("regioncollapsed", this.onRegionCollapsed, this);
30760         layout.on("regionexpanded", this.onRegionExpanded, this);
30761     },
30762     
30763     storeState : function(){
30764         this.provider.set(this.layout.id+"-layout-state", this.state);
30765     },
30766     
30767     onRegionResized : function(region, newSize){
30768         this.state[region.getPosition()].size = newSize;
30769         this.storeState();
30770     },
30771     
30772     onRegionCollapsed : function(region){
30773         this.state[region.getPosition()].collapsed = true;
30774         this.storeState();
30775     },
30776     
30777     onRegionExpanded : function(region){
30778         this.state[region.getPosition()].collapsed = false;
30779         this.storeState();
30780     }
30781 };/*
30782  * Based on:
30783  * Ext JS Library 1.1.1
30784  * Copyright(c) 2006-2007, Ext JS, LLC.
30785  *
30786  * Originally Released Under LGPL - original licence link has changed is not relivant.
30787  *
30788  * Fork - LGPL
30789  * <script type="text/javascript">
30790  */
30791 /**
30792  * @class Roo.ContentPanel
30793  * @extends Roo.util.Observable
30794  * A basic ContentPanel element.
30795  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
30796  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
30797  * @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
30798  * @cfg {Boolean}   closable      True if the panel can be closed/removed
30799  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
30800  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
30801  * @cfg {Toolbar}   toolbar       A toolbar for this panel
30802  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
30803  * @cfg {String} title          The title for this panel
30804  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
30805  * @cfg {String} url            Calls {@link #setUrl} with this value
30806  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
30807  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
30808  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
30809  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
30810
30811  * @constructor
30812  * Create a new ContentPanel.
30813  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
30814  * @param {String/Object} config A string to set only the title or a config object
30815  * @param {String} content (optional) Set the HTML content for this panel
30816  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
30817  */
30818 Roo.ContentPanel = function(el, config, content){
30819     
30820      
30821     /*
30822     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
30823         config = el;
30824         el = Roo.id();
30825     }
30826     if (config && config.parentLayout) { 
30827         el = config.parentLayout.el.createChild(); 
30828     }
30829     */
30830     if(el.autoCreate){ // xtype is available if this is called from factory
30831         config = el;
30832         el = Roo.id();
30833     }
30834     this.el = Roo.get(el);
30835     if(!this.el && config && config.autoCreate){
30836         if(typeof config.autoCreate == "object"){
30837             if(!config.autoCreate.id){
30838                 config.autoCreate.id = config.id||el;
30839             }
30840             this.el = Roo.DomHelper.append(document.body,
30841                         config.autoCreate, true);
30842         }else{
30843             this.el = Roo.DomHelper.append(document.body,
30844                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
30845         }
30846     }
30847     this.closable = false;
30848     this.loaded = false;
30849     this.active = false;
30850     if(typeof config == "string"){
30851         this.title = config;
30852     }else{
30853         Roo.apply(this, config);
30854     }
30855     
30856     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
30857         this.wrapEl = this.el.wrap();
30858         this.toolbar.container = this.el.insertSibling(false, 'before');
30859         this.toolbar = new Roo.Toolbar(this.toolbar);
30860     }
30861     
30862     // xtype created footer. - not sure if will work as we normally have to render first..
30863     if (this.footer && !this.footer.el && this.footer.xtype) {
30864         if (!this.wrapEl) {
30865             this.wrapEl = this.el.wrap();
30866         }
30867     
30868         this.footer.container = this.wrapEl.createChild();
30869          
30870         this.footer = Roo.factory(this.footer, Roo);
30871         
30872     }
30873     
30874     if(this.resizeEl){
30875         this.resizeEl = Roo.get(this.resizeEl, true);
30876     }else{
30877         this.resizeEl = this.el;
30878     }
30879     // handle view.xtype
30880     
30881  
30882     
30883     
30884     this.addEvents({
30885         /**
30886          * @event activate
30887          * Fires when this panel is activated. 
30888          * @param {Roo.ContentPanel} this
30889          */
30890         "activate" : true,
30891         /**
30892          * @event deactivate
30893          * Fires when this panel is activated. 
30894          * @param {Roo.ContentPanel} this
30895          */
30896         "deactivate" : true,
30897
30898         /**
30899          * @event resize
30900          * Fires when this panel is resized if fitToFrame is true.
30901          * @param {Roo.ContentPanel} this
30902          * @param {Number} width The width after any component adjustments
30903          * @param {Number} height The height after any component adjustments
30904          */
30905         "resize" : true,
30906         
30907          /**
30908          * @event render
30909          * Fires when this tab is created
30910          * @param {Roo.ContentPanel} this
30911          */
30912         "render" : true
30913         
30914         
30915         
30916     });
30917     
30918
30919     
30920     
30921     if(this.autoScroll){
30922         this.resizeEl.setStyle("overflow", "auto");
30923     } else {
30924         // fix randome scrolling
30925         this.el.on('scroll', function() {
30926             Roo.log('fix random scolling');
30927             this.scrollTo('top',0); 
30928         });
30929     }
30930     content = content || this.content;
30931     if(content){
30932         this.setContent(content);
30933     }
30934     if(config && config.url){
30935         this.setUrl(this.url, this.params, this.loadOnce);
30936     }
30937     
30938     
30939     
30940     Roo.ContentPanel.superclass.constructor.call(this);
30941     
30942     if (this.view && typeof(this.view.xtype) != 'undefined') {
30943         this.view.el = this.el.appendChild(document.createElement("div"));
30944         this.view = Roo.factory(this.view); 
30945         this.view.render  &&  this.view.render(false, '');  
30946     }
30947     
30948     
30949     this.fireEvent('render', this);
30950 };
30951
30952 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
30953     tabTip:'',
30954     setRegion : function(region){
30955         this.region = region;
30956         if(region){
30957            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
30958         }else{
30959            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
30960         } 
30961     },
30962     
30963     /**
30964      * Returns the toolbar for this Panel if one was configured. 
30965      * @return {Roo.Toolbar} 
30966      */
30967     getToolbar : function(){
30968         return this.toolbar;
30969     },
30970     
30971     setActiveState : function(active){
30972         this.active = active;
30973         if(!active){
30974             this.fireEvent("deactivate", this);
30975         }else{
30976             this.fireEvent("activate", this);
30977         }
30978     },
30979     /**
30980      * Updates this panel's element
30981      * @param {String} content The new content
30982      * @param {Boolean} loadScripts (optional) true to look for and process scripts
30983     */
30984     setContent : function(content, loadScripts){
30985         this.el.update(content, loadScripts);
30986     },
30987
30988     ignoreResize : function(w, h){
30989         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
30990             return true;
30991         }else{
30992             this.lastSize = {width: w, height: h};
30993             return false;
30994         }
30995     },
30996     /**
30997      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
30998      * @return {Roo.UpdateManager} The UpdateManager
30999      */
31000     getUpdateManager : function(){
31001         return this.el.getUpdateManager();
31002     },
31003      /**
31004      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31005      * @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:
31006 <pre><code>
31007 panel.load({
31008     url: "your-url.php",
31009     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31010     callback: yourFunction,
31011     scope: yourObject, //(optional scope)
31012     discardUrl: false,
31013     nocache: false,
31014     text: "Loading...",
31015     timeout: 30,
31016     scripts: false
31017 });
31018 </code></pre>
31019      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31020      * 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.
31021      * @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}
31022      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31023      * @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.
31024      * @return {Roo.ContentPanel} this
31025      */
31026     load : function(){
31027         var um = this.el.getUpdateManager();
31028         um.update.apply(um, arguments);
31029         return this;
31030     },
31031
31032
31033     /**
31034      * 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.
31035      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31036      * @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)
31037      * @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)
31038      * @return {Roo.UpdateManager} The UpdateManager
31039      */
31040     setUrl : function(url, params, loadOnce){
31041         if(this.refreshDelegate){
31042             this.removeListener("activate", this.refreshDelegate);
31043         }
31044         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31045         this.on("activate", this.refreshDelegate);
31046         return this.el.getUpdateManager();
31047     },
31048     
31049     _handleRefresh : function(url, params, loadOnce){
31050         if(!loadOnce || !this.loaded){
31051             var updater = this.el.getUpdateManager();
31052             updater.update(url, params, this._setLoaded.createDelegate(this));
31053         }
31054     },
31055     
31056     _setLoaded : function(){
31057         this.loaded = true;
31058     }, 
31059     
31060     /**
31061      * Returns this panel's id
31062      * @return {String} 
31063      */
31064     getId : function(){
31065         return this.el.id;
31066     },
31067     
31068     /** 
31069      * Returns this panel's element - used by regiosn to add.
31070      * @return {Roo.Element} 
31071      */
31072     getEl : function(){
31073         return this.wrapEl || this.el;
31074     },
31075     
31076     adjustForComponents : function(width, height)
31077     {
31078         //Roo.log('adjustForComponents ');
31079         if(this.resizeEl != this.el){
31080             width -= this.el.getFrameWidth('lr');
31081             height -= this.el.getFrameWidth('tb');
31082         }
31083         if(this.toolbar){
31084             var te = this.toolbar.getEl();
31085             height -= te.getHeight();
31086             te.setWidth(width);
31087         }
31088         if(this.footer){
31089             var te = this.footer.getEl();
31090             Roo.log("footer:" + te.getHeight());
31091             
31092             height -= te.getHeight();
31093             te.setWidth(width);
31094         }
31095         
31096         
31097         if(this.adjustments){
31098             width += this.adjustments[0];
31099             height += this.adjustments[1];
31100         }
31101         return {"width": width, "height": height};
31102     },
31103     
31104     setSize : function(width, height){
31105         if(this.fitToFrame && !this.ignoreResize(width, height)){
31106             if(this.fitContainer && this.resizeEl != this.el){
31107                 this.el.setSize(width, height);
31108             }
31109             var size = this.adjustForComponents(width, height);
31110             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31111             this.fireEvent('resize', this, size.width, size.height);
31112         }
31113     },
31114     
31115     /**
31116      * Returns this panel's title
31117      * @return {String} 
31118      */
31119     getTitle : function(){
31120         return this.title;
31121     },
31122     
31123     /**
31124      * Set this panel's title
31125      * @param {String} title
31126      */
31127     setTitle : function(title){
31128         this.title = title;
31129         if(this.region){
31130             this.region.updatePanelTitle(this, title);
31131         }
31132     },
31133     
31134     /**
31135      * Returns true is this panel was configured to be closable
31136      * @return {Boolean} 
31137      */
31138     isClosable : function(){
31139         return this.closable;
31140     },
31141     
31142     beforeSlide : function(){
31143         this.el.clip();
31144         this.resizeEl.clip();
31145     },
31146     
31147     afterSlide : function(){
31148         this.el.unclip();
31149         this.resizeEl.unclip();
31150     },
31151     
31152     /**
31153      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31154      *   Will fail silently if the {@link #setUrl} method has not been called.
31155      *   This does not activate the panel, just updates its content.
31156      */
31157     refresh : function(){
31158         if(this.refreshDelegate){
31159            this.loaded = false;
31160            this.refreshDelegate();
31161         }
31162     },
31163     
31164     /**
31165      * Destroys this panel
31166      */
31167     destroy : function(){
31168         this.el.removeAllListeners();
31169         var tempEl = document.createElement("span");
31170         tempEl.appendChild(this.el.dom);
31171         tempEl.innerHTML = "";
31172         this.el.remove();
31173         this.el = null;
31174     },
31175     
31176     /**
31177      * form - if the content panel contains a form - this is a reference to it.
31178      * @type {Roo.form.Form}
31179      */
31180     form : false,
31181     /**
31182      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31183      *    This contains a reference to it.
31184      * @type {Roo.View}
31185      */
31186     view : false,
31187     
31188       /**
31189      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31190      * <pre><code>
31191
31192 layout.addxtype({
31193        xtype : 'Form',
31194        items: [ .... ]
31195    }
31196 );
31197
31198 </code></pre>
31199      * @param {Object} cfg Xtype definition of item to add.
31200      */
31201     
31202     addxtype : function(cfg) {
31203         // add form..
31204         if (cfg.xtype.match(/^Form$/)) {
31205             
31206             var el;
31207             //if (this.footer) {
31208             //    el = this.footer.container.insertSibling(false, 'before');
31209             //} else {
31210                 el = this.el.createChild();
31211             //}
31212
31213             this.form = new  Roo.form.Form(cfg);
31214             
31215             
31216             if ( this.form.allItems.length) {
31217                 this.form.render(el.dom);
31218             }
31219             return this.form;
31220         }
31221         // should only have one of theses..
31222         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31223             // views.. should not be just added - used named prop 'view''
31224             
31225             cfg.el = this.el.appendChild(document.createElement("div"));
31226             // factory?
31227             
31228             var ret = new Roo.factory(cfg);
31229              
31230              ret.render && ret.render(false, ''); // render blank..
31231             this.view = ret;
31232             return ret;
31233         }
31234         return false;
31235     }
31236 });
31237
31238 /**
31239  * @class Roo.GridPanel
31240  * @extends Roo.ContentPanel
31241  * @constructor
31242  * Create a new GridPanel.
31243  * @param {Roo.grid.Grid} grid The grid for this panel
31244  * @param {String/Object} config A string to set only the panel's title, or a config object
31245  */
31246 Roo.GridPanel = function(grid, config){
31247     
31248   
31249     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31250         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31251         
31252     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31253     
31254     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31255     
31256     if(this.toolbar){
31257         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31258     }
31259     // xtype created footer. - not sure if will work as we normally have to render first..
31260     if (this.footer && !this.footer.el && this.footer.xtype) {
31261         
31262         this.footer.container = this.grid.getView().getFooterPanel(true);
31263         this.footer.dataSource = this.grid.dataSource;
31264         this.footer = Roo.factory(this.footer, Roo);
31265         
31266     }
31267     
31268     grid.monitorWindowResize = false; // turn off autosizing
31269     grid.autoHeight = false;
31270     grid.autoWidth = false;
31271     this.grid = grid;
31272     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31273 };
31274
31275 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31276     getId : function(){
31277         return this.grid.id;
31278     },
31279     
31280     /**
31281      * Returns the grid for this panel
31282      * @return {Roo.grid.Grid} 
31283      */
31284     getGrid : function(){
31285         return this.grid;    
31286     },
31287     
31288     setSize : function(width, height){
31289         if(!this.ignoreResize(width, height)){
31290             var grid = this.grid;
31291             var size = this.adjustForComponents(width, height);
31292             grid.getGridEl().setSize(size.width, size.height);
31293             grid.autoSize();
31294         }
31295     },
31296     
31297     beforeSlide : function(){
31298         this.grid.getView().scroller.clip();
31299     },
31300     
31301     afterSlide : function(){
31302         this.grid.getView().scroller.unclip();
31303     },
31304     
31305     destroy : function(){
31306         this.grid.destroy();
31307         delete this.grid;
31308         Roo.GridPanel.superclass.destroy.call(this); 
31309     }
31310 });
31311
31312
31313 /**
31314  * @class Roo.NestedLayoutPanel
31315  * @extends Roo.ContentPanel
31316  * @constructor
31317  * Create a new NestedLayoutPanel.
31318  * 
31319  * 
31320  * @param {Roo.BorderLayout} layout The layout for this panel
31321  * @param {String/Object} config A string to set only the title or a config object
31322  */
31323 Roo.NestedLayoutPanel = function(layout, config)
31324 {
31325     // construct with only one argument..
31326     /* FIXME - implement nicer consturctors
31327     if (layout.layout) {
31328         config = layout;
31329         layout = config.layout;
31330         delete config.layout;
31331     }
31332     if (layout.xtype && !layout.getEl) {
31333         // then layout needs constructing..
31334         layout = Roo.factory(layout, Roo);
31335     }
31336     */
31337     
31338     
31339     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31340     
31341     layout.monitorWindowResize = false; // turn off autosizing
31342     this.layout = layout;
31343     this.layout.getEl().addClass("x-layout-nested-layout");
31344     
31345     
31346     
31347     
31348 };
31349
31350 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31351
31352     setSize : function(width, height){
31353         if(!this.ignoreResize(width, height)){
31354             var size = this.adjustForComponents(width, height);
31355             var el = this.layout.getEl();
31356             el.setSize(size.width, size.height);
31357             var touch = el.dom.offsetWidth;
31358             this.layout.layout();
31359             // ie requires a double layout on the first pass
31360             if(Roo.isIE && !this.initialized){
31361                 this.initialized = true;
31362                 this.layout.layout();
31363             }
31364         }
31365     },
31366     
31367     // activate all subpanels if not currently active..
31368     
31369     setActiveState : function(active){
31370         this.active = active;
31371         if(!active){
31372             this.fireEvent("deactivate", this);
31373             return;
31374         }
31375         
31376         this.fireEvent("activate", this);
31377         // not sure if this should happen before or after..
31378         if (!this.layout) {
31379             return; // should not happen..
31380         }
31381         var reg = false;
31382         for (var r in this.layout.regions) {
31383             reg = this.layout.getRegion(r);
31384             if (reg.getActivePanel()) {
31385                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31386                 reg.setActivePanel(reg.getActivePanel());
31387                 continue;
31388             }
31389             if (!reg.panels.length) {
31390                 continue;
31391             }
31392             reg.showPanel(reg.getPanel(0));
31393         }
31394         
31395         
31396         
31397         
31398     },
31399     
31400     /**
31401      * Returns the nested BorderLayout for this panel
31402      * @return {Roo.BorderLayout} 
31403      */
31404     getLayout : function(){
31405         return this.layout;
31406     },
31407     
31408      /**
31409      * Adds a xtype elements to the layout of the nested panel
31410      * <pre><code>
31411
31412 panel.addxtype({
31413        xtype : 'ContentPanel',
31414        region: 'west',
31415        items: [ .... ]
31416    }
31417 );
31418
31419 panel.addxtype({
31420         xtype : 'NestedLayoutPanel',
31421         region: 'west',
31422         layout: {
31423            center: { },
31424            west: { }   
31425         },
31426         items : [ ... list of content panels or nested layout panels.. ]
31427    }
31428 );
31429 </code></pre>
31430      * @param {Object} cfg Xtype definition of item to add.
31431      */
31432     addxtype : function(cfg) {
31433         return this.layout.addxtype(cfg);
31434     
31435     }
31436 });
31437
31438 Roo.ScrollPanel = function(el, config, content){
31439     config = config || {};
31440     config.fitToFrame = true;
31441     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31442     
31443     this.el.dom.style.overflow = "hidden";
31444     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31445     this.el.removeClass("x-layout-inactive-content");
31446     this.el.on("mousewheel", this.onWheel, this);
31447
31448     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31449     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31450     up.unselectable(); down.unselectable();
31451     up.on("click", this.scrollUp, this);
31452     down.on("click", this.scrollDown, this);
31453     up.addClassOnOver("x-scroller-btn-over");
31454     down.addClassOnOver("x-scroller-btn-over");
31455     up.addClassOnClick("x-scroller-btn-click");
31456     down.addClassOnClick("x-scroller-btn-click");
31457     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31458
31459     this.resizeEl = this.el;
31460     this.el = wrap; this.up = up; this.down = down;
31461 };
31462
31463 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31464     increment : 100,
31465     wheelIncrement : 5,
31466     scrollUp : function(){
31467         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31468     },
31469
31470     scrollDown : function(){
31471         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31472     },
31473
31474     afterScroll : function(){
31475         var el = this.resizeEl;
31476         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31477         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31478         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31479     },
31480
31481     setSize : function(){
31482         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31483         this.afterScroll();
31484     },
31485
31486     onWheel : function(e){
31487         var d = e.getWheelDelta();
31488         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31489         this.afterScroll();
31490         e.stopEvent();
31491     },
31492
31493     setContent : function(content, loadScripts){
31494         this.resizeEl.update(content, loadScripts);
31495     }
31496
31497 });
31498
31499
31500
31501
31502
31503
31504
31505
31506
31507 /**
31508  * @class Roo.TreePanel
31509  * @extends Roo.ContentPanel
31510  * @constructor
31511  * Create a new TreePanel. - defaults to fit/scoll contents.
31512  * @param {String/Object} config A string to set only the panel's title, or a config object
31513  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31514  */
31515 Roo.TreePanel = function(config){
31516     var el = config.el;
31517     var tree = config.tree;
31518     delete config.tree; 
31519     delete config.el; // hopefull!
31520     
31521     // wrapper for IE7 strict & safari scroll issue
31522     
31523     var treeEl = el.createChild();
31524     config.resizeEl = treeEl;
31525     
31526     
31527     
31528     Roo.TreePanel.superclass.constructor.call(this, el, config);
31529  
31530  
31531     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31532     //console.log(tree);
31533     this.on('activate', function()
31534     {
31535         if (this.tree.rendered) {
31536             return;
31537         }
31538         //console.log('render tree');
31539         this.tree.render();
31540     });
31541     // this should not be needed.. - it's actually the 'el' that resizes?
31542     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
31543     
31544     //this.on('resize',  function (cp, w, h) {
31545     //        this.tree.innerCt.setWidth(w);
31546     //        this.tree.innerCt.setHeight(h);
31547     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
31548     //});
31549
31550         
31551     
31552 };
31553
31554 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31555     fitToFrame : true,
31556     autoScroll : true
31557 });
31558
31559
31560
31561
31562
31563
31564
31565
31566
31567
31568
31569 /*
31570  * Based on:
31571  * Ext JS Library 1.1.1
31572  * Copyright(c) 2006-2007, Ext JS, LLC.
31573  *
31574  * Originally Released Under LGPL - original licence link has changed is not relivant.
31575  *
31576  * Fork - LGPL
31577  * <script type="text/javascript">
31578  */
31579  
31580
31581 /**
31582  * @class Roo.ReaderLayout
31583  * @extends Roo.BorderLayout
31584  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31585  * center region containing two nested regions (a top one for a list view and one for item preview below),
31586  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31587  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31588  * expedites the setup of the overall layout and regions for this common application style.
31589  * Example:
31590  <pre><code>
31591 var reader = new Roo.ReaderLayout();
31592 var CP = Roo.ContentPanel;  // shortcut for adding
31593
31594 reader.beginUpdate();
31595 reader.add("north", new CP("north", "North"));
31596 reader.add("west", new CP("west", {title: "West"}));
31597 reader.add("east", new CP("east", {title: "East"}));
31598
31599 reader.regions.listView.add(new CP("listView", "List"));
31600 reader.regions.preview.add(new CP("preview", "Preview"));
31601 reader.endUpdate();
31602 </code></pre>
31603 * @constructor
31604 * Create a new ReaderLayout
31605 * @param {Object} config Configuration options
31606 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31607 * document.body if omitted)
31608 */
31609 Roo.ReaderLayout = function(config, renderTo){
31610     var c = config || {size:{}};
31611     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
31612         north: c.north !== false ? Roo.apply({
31613             split:false,
31614             initialSize: 32,
31615             titlebar: false
31616         }, c.north) : false,
31617         west: c.west !== false ? Roo.apply({
31618             split:true,
31619             initialSize: 200,
31620             minSize: 175,
31621             maxSize: 400,
31622             titlebar: true,
31623             collapsible: true,
31624             animate: true,
31625             margins:{left:5,right:0,bottom:5,top:5},
31626             cmargins:{left:5,right:5,bottom:5,top:5}
31627         }, c.west) : false,
31628         east: c.east !== false ? Roo.apply({
31629             split:true,
31630             initialSize: 200,
31631             minSize: 175,
31632             maxSize: 400,
31633             titlebar: true,
31634             collapsible: true,
31635             animate: true,
31636             margins:{left:0,right:5,bottom:5,top:5},
31637             cmargins:{left:5,right:5,bottom:5,top:5}
31638         }, c.east) : false,
31639         center: Roo.apply({
31640             tabPosition: 'top',
31641             autoScroll:false,
31642             closeOnTab: true,
31643             titlebar:false,
31644             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
31645         }, c.center)
31646     });
31647
31648     this.el.addClass('x-reader');
31649
31650     this.beginUpdate();
31651
31652     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
31653         south: c.preview !== false ? Roo.apply({
31654             split:true,
31655             initialSize: 200,
31656             minSize: 100,
31657             autoScroll:true,
31658             collapsible:true,
31659             titlebar: true,
31660             cmargins:{top:5,left:0, right:0, bottom:0}
31661         }, c.preview) : false,
31662         center: Roo.apply({
31663             autoScroll:false,
31664             titlebar:false,
31665             minHeight:200
31666         }, c.listView)
31667     });
31668     this.add('center', new Roo.NestedLayoutPanel(inner,
31669             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
31670
31671     this.endUpdate();
31672
31673     this.regions.preview = inner.getRegion('south');
31674     this.regions.listView = inner.getRegion('center');
31675 };
31676
31677 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
31678  * Based on:
31679  * Ext JS Library 1.1.1
31680  * Copyright(c) 2006-2007, Ext JS, LLC.
31681  *
31682  * Originally Released Under LGPL - original licence link has changed is not relivant.
31683  *
31684  * Fork - LGPL
31685  * <script type="text/javascript">
31686  */
31687  
31688 /**
31689  * @class Roo.grid.Grid
31690  * @extends Roo.util.Observable
31691  * This class represents the primary interface of a component based grid control.
31692  * <br><br>Usage:<pre><code>
31693  var grid = new Roo.grid.Grid("my-container-id", {
31694      ds: myDataStore,
31695      cm: myColModel,
31696      selModel: mySelectionModel,
31697      autoSizeColumns: true,
31698      monitorWindowResize: false,
31699      trackMouseOver: true
31700  });
31701  // set any options
31702  grid.render();
31703  * </code></pre>
31704  * <b>Common Problems:</b><br/>
31705  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
31706  * element will correct this<br/>
31707  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
31708  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
31709  * are unpredictable.<br/>
31710  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
31711  * grid to calculate dimensions/offsets.<br/>
31712   * @constructor
31713  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
31714  * The container MUST have some type of size defined for the grid to fill. The container will be
31715  * automatically set to position relative if it isn't already.
31716  * @param {Object} config A config object that sets properties on this grid.
31717  */
31718 Roo.grid.Grid = function(container, config){
31719         // initialize the container
31720         this.container = Roo.get(container);
31721         this.container.update("");
31722         this.container.setStyle("overflow", "hidden");
31723     this.container.addClass('x-grid-container');
31724
31725     this.id = this.container.id;
31726
31727     Roo.apply(this, config);
31728     // check and correct shorthanded configs
31729     if(this.ds){
31730         this.dataSource = this.ds;
31731         delete this.ds;
31732     }
31733     if(this.cm){
31734         this.colModel = this.cm;
31735         delete this.cm;
31736     }
31737     if(this.sm){
31738         this.selModel = this.sm;
31739         delete this.sm;
31740     }
31741
31742     if (this.selModel) {
31743         this.selModel = Roo.factory(this.selModel, Roo.grid);
31744         this.sm = this.selModel;
31745         this.sm.xmodule = this.xmodule || false;
31746     }
31747     if (typeof(this.colModel.config) == 'undefined') {
31748         this.colModel = new Roo.grid.ColumnModel(this.colModel);
31749         this.cm = this.colModel;
31750         this.cm.xmodule = this.xmodule || false;
31751     }
31752     if (this.dataSource) {
31753         this.dataSource= Roo.factory(this.dataSource, Roo.data);
31754         this.ds = this.dataSource;
31755         this.ds.xmodule = this.xmodule || false;
31756          
31757     }
31758     
31759     
31760     
31761     if(this.width){
31762         this.container.setWidth(this.width);
31763     }
31764
31765     if(this.height){
31766         this.container.setHeight(this.height);
31767     }
31768     /** @private */
31769         this.addEvents({
31770         // raw events
31771         /**
31772          * @event click
31773          * The raw click event for the entire grid.
31774          * @param {Roo.EventObject} e
31775          */
31776         "click" : true,
31777         /**
31778          * @event dblclick
31779          * The raw dblclick event for the entire grid.
31780          * @param {Roo.EventObject} e
31781          */
31782         "dblclick" : true,
31783         /**
31784          * @event contextmenu
31785          * The raw contextmenu event for the entire grid.
31786          * @param {Roo.EventObject} e
31787          */
31788         "contextmenu" : true,
31789         /**
31790          * @event mousedown
31791          * The raw mousedown event for the entire grid.
31792          * @param {Roo.EventObject} e
31793          */
31794         "mousedown" : true,
31795         /**
31796          * @event mouseup
31797          * The raw mouseup event for the entire grid.
31798          * @param {Roo.EventObject} e
31799          */
31800         "mouseup" : true,
31801         /**
31802          * @event mouseover
31803          * The raw mouseover event for the entire grid.
31804          * @param {Roo.EventObject} e
31805          */
31806         "mouseover" : true,
31807         /**
31808          * @event mouseout
31809          * The raw mouseout event for the entire grid.
31810          * @param {Roo.EventObject} e
31811          */
31812         "mouseout" : true,
31813         /**
31814          * @event keypress
31815          * The raw keypress event for the entire grid.
31816          * @param {Roo.EventObject} e
31817          */
31818         "keypress" : true,
31819         /**
31820          * @event keydown
31821          * The raw keydown event for the entire grid.
31822          * @param {Roo.EventObject} e
31823          */
31824         "keydown" : true,
31825
31826         // custom events
31827
31828         /**
31829          * @event cellclick
31830          * Fires when a cell is clicked
31831          * @param {Grid} this
31832          * @param {Number} rowIndex
31833          * @param {Number} columnIndex
31834          * @param {Roo.EventObject} e
31835          */
31836         "cellclick" : true,
31837         /**
31838          * @event celldblclick
31839          * Fires when a cell is double clicked
31840          * @param {Grid} this
31841          * @param {Number} rowIndex
31842          * @param {Number} columnIndex
31843          * @param {Roo.EventObject} e
31844          */
31845         "celldblclick" : true,
31846         /**
31847          * @event rowclick
31848          * Fires when a row is clicked
31849          * @param {Grid} this
31850          * @param {Number} rowIndex
31851          * @param {Roo.EventObject} e
31852          */
31853         "rowclick" : true,
31854         /**
31855          * @event rowdblclick
31856          * Fires when a row is double clicked
31857          * @param {Grid} this
31858          * @param {Number} rowIndex
31859          * @param {Roo.EventObject} e
31860          */
31861         "rowdblclick" : true,
31862         /**
31863          * @event headerclick
31864          * Fires when a header is clicked
31865          * @param {Grid} this
31866          * @param {Number} columnIndex
31867          * @param {Roo.EventObject} e
31868          */
31869         "headerclick" : true,
31870         /**
31871          * @event headerdblclick
31872          * Fires when a header cell is double clicked
31873          * @param {Grid} this
31874          * @param {Number} columnIndex
31875          * @param {Roo.EventObject} e
31876          */
31877         "headerdblclick" : true,
31878         /**
31879          * @event rowcontextmenu
31880          * Fires when a row is right clicked
31881          * @param {Grid} this
31882          * @param {Number} rowIndex
31883          * @param {Roo.EventObject} e
31884          */
31885         "rowcontextmenu" : true,
31886         /**
31887          * @event cellcontextmenu
31888          * Fires when a cell is right clicked
31889          * @param {Grid} this
31890          * @param {Number} rowIndex
31891          * @param {Number} cellIndex
31892          * @param {Roo.EventObject} e
31893          */
31894          "cellcontextmenu" : true,
31895         /**
31896          * @event headercontextmenu
31897          * Fires when a header is right clicked
31898          * @param {Grid} this
31899          * @param {Number} columnIndex
31900          * @param {Roo.EventObject} e
31901          */
31902         "headercontextmenu" : true,
31903         /**
31904          * @event bodyscroll
31905          * Fires when the body element is scrolled
31906          * @param {Number} scrollLeft
31907          * @param {Number} scrollTop
31908          */
31909         "bodyscroll" : true,
31910         /**
31911          * @event columnresize
31912          * Fires when the user resizes a column
31913          * @param {Number} columnIndex
31914          * @param {Number} newSize
31915          */
31916         "columnresize" : true,
31917         /**
31918          * @event columnmove
31919          * Fires when the user moves a column
31920          * @param {Number} oldIndex
31921          * @param {Number} newIndex
31922          */
31923         "columnmove" : true,
31924         /**
31925          * @event startdrag
31926          * Fires when row(s) start being dragged
31927          * @param {Grid} this
31928          * @param {Roo.GridDD} dd The drag drop object
31929          * @param {event} e The raw browser event
31930          */
31931         "startdrag" : true,
31932         /**
31933          * @event enddrag
31934          * Fires when a drag operation is complete
31935          * @param {Grid} this
31936          * @param {Roo.GridDD} dd The drag drop object
31937          * @param {event} e The raw browser event
31938          */
31939         "enddrag" : true,
31940         /**
31941          * @event dragdrop
31942          * Fires when dragged row(s) are dropped on a valid DD target
31943          * @param {Grid} this
31944          * @param {Roo.GridDD} dd The drag drop object
31945          * @param {String} targetId The target drag drop object
31946          * @param {event} e The raw browser event
31947          */
31948         "dragdrop" : true,
31949         /**
31950          * @event dragover
31951          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
31952          * @param {Grid} this
31953          * @param {Roo.GridDD} dd The drag drop object
31954          * @param {String} targetId The target drag drop object
31955          * @param {event} e The raw browser event
31956          */
31957         "dragover" : true,
31958         /**
31959          * @event dragenter
31960          *  Fires when the dragged row(s) first cross another DD target while being dragged
31961          * @param {Grid} this
31962          * @param {Roo.GridDD} dd The drag drop object
31963          * @param {String} targetId The target drag drop object
31964          * @param {event} e The raw browser event
31965          */
31966         "dragenter" : true,
31967         /**
31968          * @event dragout
31969          * Fires when the dragged row(s) leave another DD target while being dragged
31970          * @param {Grid} this
31971          * @param {Roo.GridDD} dd The drag drop object
31972          * @param {String} targetId The target drag drop object
31973          * @param {event} e The raw browser event
31974          */
31975         "dragout" : true,
31976         /**
31977          * @event rowclass
31978          * Fires when a row is rendered, so you can change add a style to it.
31979          * @param {GridView} gridview   The grid view
31980          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
31981          */
31982         'rowclass' : true,
31983
31984         /**
31985          * @event render
31986          * Fires when the grid is rendered
31987          * @param {Grid} grid
31988          */
31989         'render' : true
31990     });
31991
31992     Roo.grid.Grid.superclass.constructor.call(this);
31993 };
31994 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
31995     
31996     /**
31997      * @cfg {String} ddGroup - drag drop group.
31998      */
31999
32000     /**
32001      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32002      */
32003     minColumnWidth : 25,
32004
32005     /**
32006      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32007      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32008      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32009      */
32010     autoSizeColumns : false,
32011
32012     /**
32013      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32014      */
32015     autoSizeHeaders : true,
32016
32017     /**
32018      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32019      */
32020     monitorWindowResize : true,
32021
32022     /**
32023      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32024      * rows measured to get a columns size. Default is 0 (all rows).
32025      */
32026     maxRowsToMeasure : 0,
32027
32028     /**
32029      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32030      */
32031     trackMouseOver : true,
32032
32033     /**
32034     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32035     */
32036     
32037     /**
32038     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32039     */
32040     enableDragDrop : false,
32041     
32042     /**
32043     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32044     */
32045     enableColumnMove : true,
32046     
32047     /**
32048     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32049     */
32050     enableColumnHide : true,
32051     
32052     /**
32053     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32054     */
32055     enableRowHeightSync : false,
32056     
32057     /**
32058     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32059     */
32060     stripeRows : true,
32061     
32062     /**
32063     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32064     */
32065     autoHeight : false,
32066
32067     /**
32068      * @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.
32069      */
32070     autoExpandColumn : false,
32071
32072     /**
32073     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32074     * Default is 50.
32075     */
32076     autoExpandMin : 50,
32077
32078     /**
32079     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32080     */
32081     autoExpandMax : 1000,
32082
32083     /**
32084     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32085     */
32086     view : null,
32087
32088     /**
32089     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32090     */
32091     loadMask : false,
32092     /**
32093     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32094     */
32095     dropTarget: false,
32096     
32097    
32098     
32099     // private
32100     rendered : false,
32101
32102     /**
32103     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32104     * of a fixed width. Default is false.
32105     */
32106     /**
32107     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32108     */
32109     /**
32110      * Called once after all setup has been completed and the grid is ready to be rendered.
32111      * @return {Roo.grid.Grid} this
32112      */
32113     render : function()
32114     {
32115         var c = this.container;
32116         // try to detect autoHeight/width mode
32117         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32118             this.autoHeight = true;
32119         }
32120         var view = this.getView();
32121         view.init(this);
32122
32123         c.on("click", this.onClick, this);
32124         c.on("dblclick", this.onDblClick, this);
32125         c.on("contextmenu", this.onContextMenu, this);
32126         c.on("keydown", this.onKeyDown, this);
32127         if (Roo.isTouch) {
32128             c.on("touchstart", this.onTouchStart, this);
32129         }
32130
32131         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32132
32133         this.getSelectionModel().init(this);
32134
32135         view.render();
32136
32137         if(this.loadMask){
32138             this.loadMask = new Roo.LoadMask(this.container,
32139                     Roo.apply({store:this.dataSource}, this.loadMask));
32140         }
32141         
32142         
32143         if (this.toolbar && this.toolbar.xtype) {
32144             this.toolbar.container = this.getView().getHeaderPanel(true);
32145             this.toolbar = new Roo.Toolbar(this.toolbar);
32146         }
32147         if (this.footer && this.footer.xtype) {
32148             this.footer.dataSource = this.getDataSource();
32149             this.footer.container = this.getView().getFooterPanel(true);
32150             this.footer = Roo.factory(this.footer, Roo);
32151         }
32152         if (this.dropTarget && this.dropTarget.xtype) {
32153             delete this.dropTarget.xtype;
32154             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32155         }
32156         
32157         
32158         this.rendered = true;
32159         this.fireEvent('render', this);
32160         return this;
32161     },
32162
32163         /**
32164          * Reconfigures the grid to use a different Store and Column Model.
32165          * The View will be bound to the new objects and refreshed.
32166          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32167          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32168          */
32169     reconfigure : function(dataSource, colModel){
32170         if(this.loadMask){
32171             this.loadMask.destroy();
32172             this.loadMask = new Roo.LoadMask(this.container,
32173                     Roo.apply({store:dataSource}, this.loadMask));
32174         }
32175         this.view.bind(dataSource, colModel);
32176         this.dataSource = dataSource;
32177         this.colModel = colModel;
32178         this.view.refresh(true);
32179     },
32180
32181     // private
32182     onKeyDown : function(e){
32183         this.fireEvent("keydown", e);
32184     },
32185
32186     /**
32187      * Destroy this grid.
32188      * @param {Boolean} removeEl True to remove the element
32189      */
32190     destroy : function(removeEl, keepListeners){
32191         if(this.loadMask){
32192             this.loadMask.destroy();
32193         }
32194         var c = this.container;
32195         c.removeAllListeners();
32196         this.view.destroy();
32197         this.colModel.purgeListeners();
32198         if(!keepListeners){
32199             this.purgeListeners();
32200         }
32201         c.update("");
32202         if(removeEl === true){
32203             c.remove();
32204         }
32205     },
32206
32207     // private
32208     processEvent : function(name, e){
32209         // does this fire select???
32210         //Roo.log('grid:processEvent '  + name);
32211         
32212         if (name != 'touchstart' ) {
32213             this.fireEvent(name, e);    
32214         }
32215         
32216         var t = e.getTarget();
32217         var v = this.view;
32218         var header = v.findHeaderIndex(t);
32219         if(header !== false){
32220             var ename = name == 'touchstart' ? 'click' : name;
32221              
32222             this.fireEvent("header" + ename, this, header, e);
32223         }else{
32224             var row = v.findRowIndex(t);
32225             var cell = v.findCellIndex(t);
32226             if (name == 'touchstart') {
32227                 // first touch is always a click.
32228                 // hopefull this happens after selection is updated.?
32229                 name = false;
32230                 
32231                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32232                     var cs = this.selModel.getSelectedCell();
32233                     if (row == cs[0] && cell == cs[1]){
32234                         name = 'dblclick';
32235                     }
32236                 }
32237                 if (typeof(this.selModel.getSelections) != 'undefined') {
32238                     var cs = this.selModel.getSelections();
32239                     var ds = this.dataSource;
32240                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
32241                         name = 'dblclick';
32242                     }
32243                 }
32244                 if (!name) {
32245                     return;
32246                 }
32247             }
32248             
32249             
32250             if(row !== false){
32251                 this.fireEvent("row" + name, this, row, e);
32252                 if(cell !== false){
32253                     this.fireEvent("cell" + name, this, row, cell, e);
32254                 }
32255             }
32256         }
32257     },
32258
32259     // private
32260     onClick : function(e){
32261         this.processEvent("click", e);
32262     },
32263    // private
32264     onTouchStart : function(e){
32265         this.processEvent("touchstart", e);
32266     },
32267
32268     // private
32269     onContextMenu : function(e, t){
32270         this.processEvent("contextmenu", e);
32271     },
32272
32273     // private
32274     onDblClick : function(e){
32275         this.processEvent("dblclick", e);
32276     },
32277
32278     // private
32279     walkCells : function(row, col, step, fn, scope){
32280         var cm = this.colModel, clen = cm.getColumnCount();
32281         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32282         if(step < 0){
32283             if(col < 0){
32284                 row--;
32285                 first = false;
32286             }
32287             while(row >= 0){
32288                 if(!first){
32289                     col = clen-1;
32290                 }
32291                 first = false;
32292                 while(col >= 0){
32293                     if(fn.call(scope || this, row, col, cm) === true){
32294                         return [row, col];
32295                     }
32296                     col--;
32297                 }
32298                 row--;
32299             }
32300         } else {
32301             if(col >= clen){
32302                 row++;
32303                 first = false;
32304             }
32305             while(row < rlen){
32306                 if(!first){
32307                     col = 0;
32308                 }
32309                 first = false;
32310                 while(col < clen){
32311                     if(fn.call(scope || this, row, col, cm) === true){
32312                         return [row, col];
32313                     }
32314                     col++;
32315                 }
32316                 row++;
32317             }
32318         }
32319         return null;
32320     },
32321
32322     // private
32323     getSelections : function(){
32324         return this.selModel.getSelections();
32325     },
32326
32327     /**
32328      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32329      * but if manual update is required this method will initiate it.
32330      */
32331     autoSize : function(){
32332         if(this.rendered){
32333             this.view.layout();
32334             if(this.view.adjustForScroll){
32335                 this.view.adjustForScroll();
32336             }
32337         }
32338     },
32339
32340     /**
32341      * Returns the grid's underlying element.
32342      * @return {Element} The element
32343      */
32344     getGridEl : function(){
32345         return this.container;
32346     },
32347
32348     // private for compatibility, overridden by editor grid
32349     stopEditing : function(){},
32350
32351     /**
32352      * Returns the grid's SelectionModel.
32353      * @return {SelectionModel}
32354      */
32355     getSelectionModel : function(){
32356         if(!this.selModel){
32357             this.selModel = new Roo.grid.RowSelectionModel();
32358         }
32359         return this.selModel;
32360     },
32361
32362     /**
32363      * Returns the grid's DataSource.
32364      * @return {DataSource}
32365      */
32366     getDataSource : function(){
32367         return this.dataSource;
32368     },
32369
32370     /**
32371      * Returns the grid's ColumnModel.
32372      * @return {ColumnModel}
32373      */
32374     getColumnModel : function(){
32375         return this.colModel;
32376     },
32377
32378     /**
32379      * Returns the grid's GridView object.
32380      * @return {GridView}
32381      */
32382     getView : function(){
32383         if(!this.view){
32384             this.view = new Roo.grid.GridView(this.viewConfig);
32385         }
32386         return this.view;
32387     },
32388     /**
32389      * Called to get grid's drag proxy text, by default returns this.ddText.
32390      * @return {String}
32391      */
32392     getDragDropText : function(){
32393         var count = this.selModel.getCount();
32394         return String.format(this.ddText, count, count == 1 ? '' : 's');
32395     }
32396 });
32397 /**
32398  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32399  * %0 is replaced with the number of selected rows.
32400  * @type String
32401  */
32402 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32403  * Based on:
32404  * Ext JS Library 1.1.1
32405  * Copyright(c) 2006-2007, Ext JS, LLC.
32406  *
32407  * Originally Released Under LGPL - original licence link has changed is not relivant.
32408  *
32409  * Fork - LGPL
32410  * <script type="text/javascript">
32411  */
32412  
32413 Roo.grid.AbstractGridView = function(){
32414         this.grid = null;
32415         
32416         this.events = {
32417             "beforerowremoved" : true,
32418             "beforerowsinserted" : true,
32419             "beforerefresh" : true,
32420             "rowremoved" : true,
32421             "rowsinserted" : true,
32422             "rowupdated" : true,
32423             "refresh" : true
32424         };
32425     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32426 };
32427
32428 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32429     rowClass : "x-grid-row",
32430     cellClass : "x-grid-cell",
32431     tdClass : "x-grid-td",
32432     hdClass : "x-grid-hd",
32433     splitClass : "x-grid-hd-split",
32434     
32435     init: function(grid){
32436         this.grid = grid;
32437                 var cid = this.grid.getGridEl().id;
32438         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32439         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32440         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32441         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32442         },
32443         
32444     getColumnRenderers : function(){
32445         var renderers = [];
32446         var cm = this.grid.colModel;
32447         var colCount = cm.getColumnCount();
32448         for(var i = 0; i < colCount; i++){
32449             renderers[i] = cm.getRenderer(i);
32450         }
32451         return renderers;
32452     },
32453     
32454     getColumnIds : function(){
32455         var ids = [];
32456         var cm = this.grid.colModel;
32457         var colCount = cm.getColumnCount();
32458         for(var i = 0; i < colCount; i++){
32459             ids[i] = cm.getColumnId(i);
32460         }
32461         return ids;
32462     },
32463     
32464     getDataIndexes : function(){
32465         if(!this.indexMap){
32466             this.indexMap = this.buildIndexMap();
32467         }
32468         return this.indexMap.colToData;
32469     },
32470     
32471     getColumnIndexByDataIndex : function(dataIndex){
32472         if(!this.indexMap){
32473             this.indexMap = this.buildIndexMap();
32474         }
32475         return this.indexMap.dataToCol[dataIndex];
32476     },
32477     
32478     /**
32479      * Set a css style for a column dynamically. 
32480      * @param {Number} colIndex The index of the column
32481      * @param {String} name The css property name
32482      * @param {String} value The css value
32483      */
32484     setCSSStyle : function(colIndex, name, value){
32485         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32486         Roo.util.CSS.updateRule(selector, name, value);
32487     },
32488     
32489     generateRules : function(cm){
32490         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32491         Roo.util.CSS.removeStyleSheet(rulesId);
32492         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32493             var cid = cm.getColumnId(i);
32494             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32495                          this.tdSelector, cid, " {\n}\n",
32496                          this.hdSelector, cid, " {\n}\n",
32497                          this.splitSelector, cid, " {\n}\n");
32498         }
32499         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32500     }
32501 });/*
32502  * Based on:
32503  * Ext JS Library 1.1.1
32504  * Copyright(c) 2006-2007, Ext JS, LLC.
32505  *
32506  * Originally Released Under LGPL - original licence link has changed is not relivant.
32507  *
32508  * Fork - LGPL
32509  * <script type="text/javascript">
32510  */
32511
32512 // private
32513 // This is a support class used internally by the Grid components
32514 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32515     this.grid = grid;
32516     this.view = grid.getView();
32517     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32518     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32519     if(hd2){
32520         this.setHandleElId(Roo.id(hd));
32521         this.setOuterHandleElId(Roo.id(hd2));
32522     }
32523     this.scroll = false;
32524 };
32525 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32526     maxDragWidth: 120,
32527     getDragData : function(e){
32528         var t = Roo.lib.Event.getTarget(e);
32529         var h = this.view.findHeaderCell(t);
32530         if(h){
32531             return {ddel: h.firstChild, header:h};
32532         }
32533         return false;
32534     },
32535
32536     onInitDrag : function(e){
32537         this.view.headersDisabled = true;
32538         var clone = this.dragData.ddel.cloneNode(true);
32539         clone.id = Roo.id();
32540         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32541         this.proxy.update(clone);
32542         return true;
32543     },
32544
32545     afterValidDrop : function(){
32546         var v = this.view;
32547         setTimeout(function(){
32548             v.headersDisabled = false;
32549         }, 50);
32550     },
32551
32552     afterInvalidDrop : function(){
32553         var v = this.view;
32554         setTimeout(function(){
32555             v.headersDisabled = false;
32556         }, 50);
32557     }
32558 });
32559 /*
32560  * Based on:
32561  * Ext JS Library 1.1.1
32562  * Copyright(c) 2006-2007, Ext JS, LLC.
32563  *
32564  * Originally Released Under LGPL - original licence link has changed is not relivant.
32565  *
32566  * Fork - LGPL
32567  * <script type="text/javascript">
32568  */
32569 // private
32570 // This is a support class used internally by the Grid components
32571 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32572     this.grid = grid;
32573     this.view = grid.getView();
32574     // split the proxies so they don't interfere with mouse events
32575     this.proxyTop = Roo.DomHelper.append(document.body, {
32576         cls:"col-move-top", html:"&#160;"
32577     }, true);
32578     this.proxyBottom = Roo.DomHelper.append(document.body, {
32579         cls:"col-move-bottom", html:"&#160;"
32580     }, true);
32581     this.proxyTop.hide = this.proxyBottom.hide = function(){
32582         this.setLeftTop(-100,-100);
32583         this.setStyle("visibility", "hidden");
32584     };
32585     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32586     // temporarily disabled
32587     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
32588     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
32589 };
32590 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
32591     proxyOffsets : [-4, -9],
32592     fly: Roo.Element.fly,
32593
32594     getTargetFromEvent : function(e){
32595         var t = Roo.lib.Event.getTarget(e);
32596         var cindex = this.view.findCellIndex(t);
32597         if(cindex !== false){
32598             return this.view.getHeaderCell(cindex);
32599         }
32600         return null;
32601     },
32602
32603     nextVisible : function(h){
32604         var v = this.view, cm = this.grid.colModel;
32605         h = h.nextSibling;
32606         while(h){
32607             if(!cm.isHidden(v.getCellIndex(h))){
32608                 return h;
32609             }
32610             h = h.nextSibling;
32611         }
32612         return null;
32613     },
32614
32615     prevVisible : function(h){
32616         var v = this.view, cm = this.grid.colModel;
32617         h = h.prevSibling;
32618         while(h){
32619             if(!cm.isHidden(v.getCellIndex(h))){
32620                 return h;
32621             }
32622             h = h.prevSibling;
32623         }
32624         return null;
32625     },
32626
32627     positionIndicator : function(h, n, e){
32628         var x = Roo.lib.Event.getPageX(e);
32629         var r = Roo.lib.Dom.getRegion(n.firstChild);
32630         var px, pt, py = r.top + this.proxyOffsets[1];
32631         if((r.right - x) <= (r.right-r.left)/2){
32632             px = r.right+this.view.borderWidth;
32633             pt = "after";
32634         }else{
32635             px = r.left;
32636             pt = "before";
32637         }
32638         var oldIndex = this.view.getCellIndex(h);
32639         var newIndex = this.view.getCellIndex(n);
32640
32641         if(this.grid.colModel.isFixed(newIndex)){
32642             return false;
32643         }
32644
32645         var locked = this.grid.colModel.isLocked(newIndex);
32646
32647         if(pt == "after"){
32648             newIndex++;
32649         }
32650         if(oldIndex < newIndex){
32651             newIndex--;
32652         }
32653         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
32654             return false;
32655         }
32656         px +=  this.proxyOffsets[0];
32657         this.proxyTop.setLeftTop(px, py);
32658         this.proxyTop.show();
32659         if(!this.bottomOffset){
32660             this.bottomOffset = this.view.mainHd.getHeight();
32661         }
32662         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
32663         this.proxyBottom.show();
32664         return pt;
32665     },
32666
32667     onNodeEnter : function(n, dd, e, data){
32668         if(data.header != n){
32669             this.positionIndicator(data.header, n, e);
32670         }
32671     },
32672
32673     onNodeOver : function(n, dd, e, data){
32674         var result = false;
32675         if(data.header != n){
32676             result = this.positionIndicator(data.header, n, e);
32677         }
32678         if(!result){
32679             this.proxyTop.hide();
32680             this.proxyBottom.hide();
32681         }
32682         return result ? this.dropAllowed : this.dropNotAllowed;
32683     },
32684
32685     onNodeOut : function(n, dd, e, data){
32686         this.proxyTop.hide();
32687         this.proxyBottom.hide();
32688     },
32689
32690     onNodeDrop : function(n, dd, e, data){
32691         var h = data.header;
32692         if(h != n){
32693             var cm = this.grid.colModel;
32694             var x = Roo.lib.Event.getPageX(e);
32695             var r = Roo.lib.Dom.getRegion(n.firstChild);
32696             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
32697             var oldIndex = this.view.getCellIndex(h);
32698             var newIndex = this.view.getCellIndex(n);
32699             var locked = cm.isLocked(newIndex);
32700             if(pt == "after"){
32701                 newIndex++;
32702             }
32703             if(oldIndex < newIndex){
32704                 newIndex--;
32705             }
32706             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
32707                 return false;
32708             }
32709             cm.setLocked(oldIndex, locked, true);
32710             cm.moveColumn(oldIndex, newIndex);
32711             this.grid.fireEvent("columnmove", oldIndex, newIndex);
32712             return true;
32713         }
32714         return false;
32715     }
32716 });
32717 /*
32718  * Based on:
32719  * Ext JS Library 1.1.1
32720  * Copyright(c) 2006-2007, Ext JS, LLC.
32721  *
32722  * Originally Released Under LGPL - original licence link has changed is not relivant.
32723  *
32724  * Fork - LGPL
32725  * <script type="text/javascript">
32726  */
32727   
32728 /**
32729  * @class Roo.grid.GridView
32730  * @extends Roo.util.Observable
32731  *
32732  * @constructor
32733  * @param {Object} config
32734  */
32735 Roo.grid.GridView = function(config){
32736     Roo.grid.GridView.superclass.constructor.call(this);
32737     this.el = null;
32738
32739     Roo.apply(this, config);
32740 };
32741
32742 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
32743
32744     unselectable :  'unselectable="on"',
32745     unselectableCls :  'x-unselectable',
32746     
32747     
32748     rowClass : "x-grid-row",
32749
32750     cellClass : "x-grid-col",
32751
32752     tdClass : "x-grid-td",
32753
32754     hdClass : "x-grid-hd",
32755
32756     splitClass : "x-grid-split",
32757
32758     sortClasses : ["sort-asc", "sort-desc"],
32759
32760     enableMoveAnim : false,
32761
32762     hlColor: "C3DAF9",
32763
32764     dh : Roo.DomHelper,
32765
32766     fly : Roo.Element.fly,
32767
32768     css : Roo.util.CSS,
32769
32770     borderWidth: 1,
32771
32772     splitOffset: 3,
32773
32774     scrollIncrement : 22,
32775
32776     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
32777
32778     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
32779
32780     bind : function(ds, cm){
32781         if(this.ds){
32782             this.ds.un("load", this.onLoad, this);
32783             this.ds.un("datachanged", this.onDataChange, this);
32784             this.ds.un("add", this.onAdd, this);
32785             this.ds.un("remove", this.onRemove, this);
32786             this.ds.un("update", this.onUpdate, this);
32787             this.ds.un("clear", this.onClear, this);
32788         }
32789         if(ds){
32790             ds.on("load", this.onLoad, this);
32791             ds.on("datachanged", this.onDataChange, this);
32792             ds.on("add", this.onAdd, this);
32793             ds.on("remove", this.onRemove, this);
32794             ds.on("update", this.onUpdate, this);
32795             ds.on("clear", this.onClear, this);
32796         }
32797         this.ds = ds;
32798
32799         if(this.cm){
32800             this.cm.un("widthchange", this.onColWidthChange, this);
32801             this.cm.un("headerchange", this.onHeaderChange, this);
32802             this.cm.un("hiddenchange", this.onHiddenChange, this);
32803             this.cm.un("columnmoved", this.onColumnMove, this);
32804             this.cm.un("columnlockchange", this.onColumnLock, this);
32805         }
32806         if(cm){
32807             this.generateRules(cm);
32808             cm.on("widthchange", this.onColWidthChange, this);
32809             cm.on("headerchange", this.onHeaderChange, this);
32810             cm.on("hiddenchange", this.onHiddenChange, this);
32811             cm.on("columnmoved", this.onColumnMove, this);
32812             cm.on("columnlockchange", this.onColumnLock, this);
32813         }
32814         this.cm = cm;
32815     },
32816
32817     init: function(grid){
32818         Roo.grid.GridView.superclass.init.call(this, grid);
32819
32820         this.bind(grid.dataSource, grid.colModel);
32821
32822         grid.on("headerclick", this.handleHeaderClick, this);
32823
32824         if(grid.trackMouseOver){
32825             grid.on("mouseover", this.onRowOver, this);
32826             grid.on("mouseout", this.onRowOut, this);
32827         }
32828         grid.cancelTextSelection = function(){};
32829         this.gridId = grid.id;
32830
32831         var tpls = this.templates || {};
32832
32833         if(!tpls.master){
32834             tpls.master = new Roo.Template(
32835                '<div class="x-grid" hidefocus="true">',
32836                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
32837                   '<div class="x-grid-topbar"></div>',
32838                   '<div class="x-grid-scroller"><div></div></div>',
32839                   '<div class="x-grid-locked">',
32840                       '<div class="x-grid-header">{lockedHeader}</div>',
32841                       '<div class="x-grid-body">{lockedBody}</div>',
32842                   "</div>",
32843                   '<div class="x-grid-viewport">',
32844                       '<div class="x-grid-header">{header}</div>',
32845                       '<div class="x-grid-body">{body}</div>',
32846                   "</div>",
32847                   '<div class="x-grid-bottombar"></div>',
32848                  
32849                   '<div class="x-grid-resize-proxy">&#160;</div>',
32850                "</div>"
32851             );
32852             tpls.master.disableformats = true;
32853         }
32854
32855         if(!tpls.header){
32856             tpls.header = new Roo.Template(
32857                '<table border="0" cellspacing="0" cellpadding="0">',
32858                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
32859                "</table>{splits}"
32860             );
32861             tpls.header.disableformats = true;
32862         }
32863         tpls.header.compile();
32864
32865         if(!tpls.hcell){
32866             tpls.hcell = new Roo.Template(
32867                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
32868                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
32869                 "</div></td>"
32870              );
32871              tpls.hcell.disableFormats = true;
32872         }
32873         tpls.hcell.compile();
32874
32875         if(!tpls.hsplit){
32876             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
32877                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
32878             tpls.hsplit.disableFormats = true;
32879         }
32880         tpls.hsplit.compile();
32881
32882         if(!tpls.body){
32883             tpls.body = new Roo.Template(
32884                '<table border="0" cellspacing="0" cellpadding="0">',
32885                "<tbody>{rows}</tbody>",
32886                "</table>"
32887             );
32888             tpls.body.disableFormats = true;
32889         }
32890         tpls.body.compile();
32891
32892         if(!tpls.row){
32893             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
32894             tpls.row.disableFormats = true;
32895         }
32896         tpls.row.compile();
32897
32898         if(!tpls.cell){
32899             tpls.cell = new Roo.Template(
32900                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
32901                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
32902                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
32903                 "</td>"
32904             );
32905             tpls.cell.disableFormats = true;
32906         }
32907         tpls.cell.compile();
32908
32909         this.templates = tpls;
32910     },
32911
32912     // remap these for backwards compat
32913     onColWidthChange : function(){
32914         this.updateColumns.apply(this, arguments);
32915     },
32916     onHeaderChange : function(){
32917         this.updateHeaders.apply(this, arguments);
32918     }, 
32919     onHiddenChange : function(){
32920         this.handleHiddenChange.apply(this, arguments);
32921     },
32922     onColumnMove : function(){
32923         this.handleColumnMove.apply(this, arguments);
32924     },
32925     onColumnLock : function(){
32926         this.handleLockChange.apply(this, arguments);
32927     },
32928
32929     onDataChange : function(){
32930         this.refresh();
32931         this.updateHeaderSortState();
32932     },
32933
32934     onClear : function(){
32935         this.refresh();
32936     },
32937
32938     onUpdate : function(ds, record){
32939         this.refreshRow(record);
32940     },
32941
32942     refreshRow : function(record){
32943         var ds = this.ds, index;
32944         if(typeof record == 'number'){
32945             index = record;
32946             record = ds.getAt(index);
32947         }else{
32948             index = ds.indexOf(record);
32949         }
32950         this.insertRows(ds, index, index, true);
32951         this.onRemove(ds, record, index+1, true);
32952         this.syncRowHeights(index, index);
32953         this.layout();
32954         this.fireEvent("rowupdated", this, index, record);
32955     },
32956
32957     onAdd : function(ds, records, index){
32958         this.insertRows(ds, index, index + (records.length-1));
32959     },
32960
32961     onRemove : function(ds, record, index, isUpdate){
32962         if(isUpdate !== true){
32963             this.fireEvent("beforerowremoved", this, index, record);
32964         }
32965         var bt = this.getBodyTable(), lt = this.getLockedTable();
32966         if(bt.rows[index]){
32967             bt.firstChild.removeChild(bt.rows[index]);
32968         }
32969         if(lt.rows[index]){
32970             lt.firstChild.removeChild(lt.rows[index]);
32971         }
32972         if(isUpdate !== true){
32973             this.stripeRows(index);
32974             this.syncRowHeights(index, index);
32975             this.layout();
32976             this.fireEvent("rowremoved", this, index, record);
32977         }
32978     },
32979
32980     onLoad : function(){
32981         this.scrollToTop();
32982     },
32983
32984     /**
32985      * Scrolls the grid to the top
32986      */
32987     scrollToTop : function(){
32988         if(this.scroller){
32989             this.scroller.dom.scrollTop = 0;
32990             this.syncScroll();
32991         }
32992     },
32993
32994     /**
32995      * Gets a panel in the header of the grid that can be used for toolbars etc.
32996      * After modifying the contents of this panel a call to grid.autoSize() may be
32997      * required to register any changes in size.
32998      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
32999      * @return Roo.Element
33000      */
33001     getHeaderPanel : function(doShow){
33002         if(doShow){
33003             this.headerPanel.show();
33004         }
33005         return this.headerPanel;
33006     },
33007
33008     /**
33009      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33010      * After modifying the contents of this panel a call to grid.autoSize() may be
33011      * required to register any changes in size.
33012      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33013      * @return Roo.Element
33014      */
33015     getFooterPanel : function(doShow){
33016         if(doShow){
33017             this.footerPanel.show();
33018         }
33019         return this.footerPanel;
33020     },
33021
33022     initElements : function(){
33023         var E = Roo.Element;
33024         var el = this.grid.getGridEl().dom.firstChild;
33025         var cs = el.childNodes;
33026
33027         this.el = new E(el);
33028         
33029          this.focusEl = new E(el.firstChild);
33030         this.focusEl.swallowEvent("click", true);
33031         
33032         this.headerPanel = new E(cs[1]);
33033         this.headerPanel.enableDisplayMode("block");
33034
33035         this.scroller = new E(cs[2]);
33036         this.scrollSizer = new E(this.scroller.dom.firstChild);
33037
33038         this.lockedWrap = new E(cs[3]);
33039         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33040         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33041
33042         this.mainWrap = new E(cs[4]);
33043         this.mainHd = new E(this.mainWrap.dom.firstChild);
33044         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33045
33046         this.footerPanel = new E(cs[5]);
33047         this.footerPanel.enableDisplayMode("block");
33048
33049         this.resizeProxy = new E(cs[6]);
33050
33051         this.headerSelector = String.format(
33052            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33053            this.lockedHd.id, this.mainHd.id
33054         );
33055
33056         this.splitterSelector = String.format(
33057            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33058            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33059         );
33060     },
33061     idToCssName : function(s)
33062     {
33063         return s.replace(/[^a-z0-9]+/ig, '-');
33064     },
33065
33066     getHeaderCell : function(index){
33067         return Roo.DomQuery.select(this.headerSelector)[index];
33068     },
33069
33070     getHeaderCellMeasure : function(index){
33071         return this.getHeaderCell(index).firstChild;
33072     },
33073
33074     getHeaderCellText : function(index){
33075         return this.getHeaderCell(index).firstChild.firstChild;
33076     },
33077
33078     getLockedTable : function(){
33079         return this.lockedBody.dom.firstChild;
33080     },
33081
33082     getBodyTable : function(){
33083         return this.mainBody.dom.firstChild;
33084     },
33085
33086     getLockedRow : function(index){
33087         return this.getLockedTable().rows[index];
33088     },
33089
33090     getRow : function(index){
33091         return this.getBodyTable().rows[index];
33092     },
33093
33094     getRowComposite : function(index){
33095         if(!this.rowEl){
33096             this.rowEl = new Roo.CompositeElementLite();
33097         }
33098         var els = [], lrow, mrow;
33099         if(lrow = this.getLockedRow(index)){
33100             els.push(lrow);
33101         }
33102         if(mrow = this.getRow(index)){
33103             els.push(mrow);
33104         }
33105         this.rowEl.elements = els;
33106         return this.rowEl;
33107     },
33108     /**
33109      * Gets the 'td' of the cell
33110      * 
33111      * @param {Integer} rowIndex row to select
33112      * @param {Integer} colIndex column to select
33113      * 
33114      * @return {Object} 
33115      */
33116     getCell : function(rowIndex, colIndex){
33117         var locked = this.cm.getLockedCount();
33118         var source;
33119         if(colIndex < locked){
33120             source = this.lockedBody.dom.firstChild;
33121         }else{
33122             source = this.mainBody.dom.firstChild;
33123             colIndex -= locked;
33124         }
33125         return source.rows[rowIndex].childNodes[colIndex];
33126     },
33127
33128     getCellText : function(rowIndex, colIndex){
33129         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33130     },
33131
33132     getCellBox : function(cell){
33133         var b = this.fly(cell).getBox();
33134         if(Roo.isOpera){ // opera fails to report the Y
33135             b.y = cell.offsetTop + this.mainBody.getY();
33136         }
33137         return b;
33138     },
33139
33140     getCellIndex : function(cell){
33141         var id = String(cell.className).match(this.cellRE);
33142         if(id){
33143             return parseInt(id[1], 10);
33144         }
33145         return 0;
33146     },
33147
33148     findHeaderIndex : function(n){
33149         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33150         return r ? this.getCellIndex(r) : false;
33151     },
33152
33153     findHeaderCell : function(n){
33154         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33155         return r ? r : false;
33156     },
33157
33158     findRowIndex : function(n){
33159         if(!n){
33160             return false;
33161         }
33162         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33163         return r ? r.rowIndex : false;
33164     },
33165
33166     findCellIndex : function(node){
33167         var stop = this.el.dom;
33168         while(node && node != stop){
33169             if(this.findRE.test(node.className)){
33170                 return this.getCellIndex(node);
33171             }
33172             node = node.parentNode;
33173         }
33174         return false;
33175     },
33176
33177     getColumnId : function(index){
33178         return this.cm.getColumnId(index);
33179     },
33180
33181     getSplitters : function()
33182     {
33183         if(this.splitterSelector){
33184            return Roo.DomQuery.select(this.splitterSelector);
33185         }else{
33186             return null;
33187       }
33188     },
33189
33190     getSplitter : function(index){
33191         return this.getSplitters()[index];
33192     },
33193
33194     onRowOver : function(e, t){
33195         var row;
33196         if((row = this.findRowIndex(t)) !== false){
33197             this.getRowComposite(row).addClass("x-grid-row-over");
33198         }
33199     },
33200
33201     onRowOut : function(e, t){
33202         var row;
33203         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33204             this.getRowComposite(row).removeClass("x-grid-row-over");
33205         }
33206     },
33207
33208     renderHeaders : function(){
33209         var cm = this.cm;
33210         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33211         var cb = [], lb = [], sb = [], lsb = [], p = {};
33212         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33213             p.cellId = "x-grid-hd-0-" + i;
33214             p.splitId = "x-grid-csplit-0-" + i;
33215             p.id = cm.getColumnId(i);
33216             p.value = cm.getColumnHeader(i) || "";
33217             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
33218             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33219             if(!cm.isLocked(i)){
33220                 cb[cb.length] = ct.apply(p);
33221                 sb[sb.length] = st.apply(p);
33222             }else{
33223                 lb[lb.length] = ct.apply(p);
33224                 lsb[lsb.length] = st.apply(p);
33225             }
33226         }
33227         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33228                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33229     },
33230
33231     updateHeaders : function(){
33232         var html = this.renderHeaders();
33233         this.lockedHd.update(html[0]);
33234         this.mainHd.update(html[1]);
33235     },
33236
33237     /**
33238      * Focuses the specified row.
33239      * @param {Number} row The row index
33240      */
33241     focusRow : function(row)
33242     {
33243         //Roo.log('GridView.focusRow');
33244         var x = this.scroller.dom.scrollLeft;
33245         this.focusCell(row, 0, false);
33246         this.scroller.dom.scrollLeft = x;
33247     },
33248
33249     /**
33250      * Focuses the specified cell.
33251      * @param {Number} row The row index
33252      * @param {Number} col The column index
33253      * @param {Boolean} hscroll false to disable horizontal scrolling
33254      */
33255     focusCell : function(row, col, hscroll)
33256     {
33257         //Roo.log('GridView.focusCell');
33258         var el = this.ensureVisible(row, col, hscroll);
33259         this.focusEl.alignTo(el, "tl-tl");
33260         if(Roo.isGecko){
33261             this.focusEl.focus();
33262         }else{
33263             this.focusEl.focus.defer(1, this.focusEl);
33264         }
33265     },
33266
33267     /**
33268      * Scrolls the specified cell into view
33269      * @param {Number} row The row index
33270      * @param {Number} col The column index
33271      * @param {Boolean} hscroll false to disable horizontal scrolling
33272      */
33273     ensureVisible : function(row, col, hscroll)
33274     {
33275         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33276         //return null; //disable for testing.
33277         if(typeof row != "number"){
33278             row = row.rowIndex;
33279         }
33280         if(row < 0 && row >= this.ds.getCount()){
33281             return  null;
33282         }
33283         col = (col !== undefined ? col : 0);
33284         var cm = this.grid.colModel;
33285         while(cm.isHidden(col)){
33286             col++;
33287         }
33288
33289         var el = this.getCell(row, col);
33290         if(!el){
33291             return null;
33292         }
33293         var c = this.scroller.dom;
33294
33295         var ctop = parseInt(el.offsetTop, 10);
33296         var cleft = parseInt(el.offsetLeft, 10);
33297         var cbot = ctop + el.offsetHeight;
33298         var cright = cleft + el.offsetWidth;
33299         
33300         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33301         var stop = parseInt(c.scrollTop, 10);
33302         var sleft = parseInt(c.scrollLeft, 10);
33303         var sbot = stop + ch;
33304         var sright = sleft + c.clientWidth;
33305         /*
33306         Roo.log('GridView.ensureVisible:' +
33307                 ' ctop:' + ctop +
33308                 ' c.clientHeight:' + c.clientHeight +
33309                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33310                 ' stop:' + stop +
33311                 ' cbot:' + cbot +
33312                 ' sbot:' + sbot +
33313                 ' ch:' + ch  
33314                 );
33315         */
33316         if(ctop < stop){
33317              c.scrollTop = ctop;
33318             //Roo.log("set scrolltop to ctop DISABLE?");
33319         }else if(cbot > sbot){
33320             //Roo.log("set scrolltop to cbot-ch");
33321             c.scrollTop = cbot-ch;
33322         }
33323         
33324         if(hscroll !== false){
33325             if(cleft < sleft){
33326                 c.scrollLeft = cleft;
33327             }else if(cright > sright){
33328                 c.scrollLeft = cright-c.clientWidth;
33329             }
33330         }
33331          
33332         return el;
33333     },
33334
33335     updateColumns : function(){
33336         this.grid.stopEditing();
33337         var cm = this.grid.colModel, colIds = this.getColumnIds();
33338         //var totalWidth = cm.getTotalWidth();
33339         var pos = 0;
33340         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33341             //if(cm.isHidden(i)) continue;
33342             var w = cm.getColumnWidth(i);
33343             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33344             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33345         }
33346         this.updateSplitters();
33347     },
33348
33349     generateRules : function(cm){
33350         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33351         Roo.util.CSS.removeStyleSheet(rulesId);
33352         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33353             var cid = cm.getColumnId(i);
33354             var align = '';
33355             if(cm.config[i].align){
33356                 align = 'text-align:'+cm.config[i].align+';';
33357             }
33358             var hidden = '';
33359             if(cm.isHidden(i)){
33360                 hidden = 'display:none;';
33361             }
33362             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33363             ruleBuf.push(
33364                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33365                     this.hdSelector, cid, " {\n", align, width, "}\n",
33366                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33367                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33368         }
33369         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33370     },
33371
33372     updateSplitters : function(){
33373         var cm = this.cm, s = this.getSplitters();
33374         if(s){ // splitters not created yet
33375             var pos = 0, locked = true;
33376             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33377                 if(cm.isHidden(i)) {
33378                     continue;
33379                 }
33380                 var w = cm.getColumnWidth(i); // make sure it's a number
33381                 if(!cm.isLocked(i) && locked){
33382                     pos = 0;
33383                     locked = false;
33384                 }
33385                 pos += w;
33386                 s[i].style.left = (pos-this.splitOffset) + "px";
33387             }
33388         }
33389     },
33390
33391     handleHiddenChange : function(colModel, colIndex, hidden){
33392         if(hidden){
33393             this.hideColumn(colIndex);
33394         }else{
33395             this.unhideColumn(colIndex);
33396         }
33397     },
33398
33399     hideColumn : function(colIndex){
33400         var cid = this.getColumnId(colIndex);
33401         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33402         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33403         if(Roo.isSafari){
33404             this.updateHeaders();
33405         }
33406         this.updateSplitters();
33407         this.layout();
33408     },
33409
33410     unhideColumn : function(colIndex){
33411         var cid = this.getColumnId(colIndex);
33412         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33413         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33414
33415         if(Roo.isSafari){
33416             this.updateHeaders();
33417         }
33418         this.updateSplitters();
33419         this.layout();
33420     },
33421
33422     insertRows : function(dm, firstRow, lastRow, isUpdate){
33423         if(firstRow == 0 && lastRow == dm.getCount()-1){
33424             this.refresh();
33425         }else{
33426             if(!isUpdate){
33427                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33428             }
33429             var s = this.getScrollState();
33430             var markup = this.renderRows(firstRow, lastRow);
33431             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33432             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33433             this.restoreScroll(s);
33434             if(!isUpdate){
33435                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33436                 this.syncRowHeights(firstRow, lastRow);
33437                 this.stripeRows(firstRow);
33438                 this.layout();
33439             }
33440         }
33441     },
33442
33443     bufferRows : function(markup, target, index){
33444         var before = null, trows = target.rows, tbody = target.tBodies[0];
33445         if(index < trows.length){
33446             before = trows[index];
33447         }
33448         var b = document.createElement("div");
33449         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33450         var rows = b.firstChild.rows;
33451         for(var i = 0, len = rows.length; i < len; i++){
33452             if(before){
33453                 tbody.insertBefore(rows[0], before);
33454             }else{
33455                 tbody.appendChild(rows[0]);
33456             }
33457         }
33458         b.innerHTML = "";
33459         b = null;
33460     },
33461
33462     deleteRows : function(dm, firstRow, lastRow){
33463         if(dm.getRowCount()<1){
33464             this.fireEvent("beforerefresh", this);
33465             this.mainBody.update("");
33466             this.lockedBody.update("");
33467             this.fireEvent("refresh", this);
33468         }else{
33469             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33470             var bt = this.getBodyTable();
33471             var tbody = bt.firstChild;
33472             var rows = bt.rows;
33473             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33474                 tbody.removeChild(rows[firstRow]);
33475             }
33476             this.stripeRows(firstRow);
33477             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33478         }
33479     },
33480
33481     updateRows : function(dataSource, firstRow, lastRow){
33482         var s = this.getScrollState();
33483         this.refresh();
33484         this.restoreScroll(s);
33485     },
33486
33487     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33488         if(!noRefresh){
33489            this.refresh();
33490         }
33491         this.updateHeaderSortState();
33492     },
33493
33494     getScrollState : function(){
33495         
33496         var sb = this.scroller.dom;
33497         return {left: sb.scrollLeft, top: sb.scrollTop};
33498     },
33499
33500     stripeRows : function(startRow){
33501         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33502             return;
33503         }
33504         startRow = startRow || 0;
33505         var rows = this.getBodyTable().rows;
33506         var lrows = this.getLockedTable().rows;
33507         var cls = ' x-grid-row-alt ';
33508         for(var i = startRow, len = rows.length; i < len; i++){
33509             var row = rows[i], lrow = lrows[i];
33510             var isAlt = ((i+1) % 2 == 0);
33511             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33512             if(isAlt == hasAlt){
33513                 continue;
33514             }
33515             if(isAlt){
33516                 row.className += " x-grid-row-alt";
33517             }else{
33518                 row.className = row.className.replace("x-grid-row-alt", "");
33519             }
33520             if(lrow){
33521                 lrow.className = row.className;
33522             }
33523         }
33524     },
33525
33526     restoreScroll : function(state){
33527         //Roo.log('GridView.restoreScroll');
33528         var sb = this.scroller.dom;
33529         sb.scrollLeft = state.left;
33530         sb.scrollTop = state.top;
33531         this.syncScroll();
33532     },
33533
33534     syncScroll : function(){
33535         //Roo.log('GridView.syncScroll');
33536         var sb = this.scroller.dom;
33537         var sh = this.mainHd.dom;
33538         var bs = this.mainBody.dom;
33539         var lv = this.lockedBody.dom;
33540         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33541         lv.scrollTop = bs.scrollTop = sb.scrollTop;
33542     },
33543
33544     handleScroll : function(e){
33545         this.syncScroll();
33546         var sb = this.scroller.dom;
33547         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33548         e.stopEvent();
33549     },
33550
33551     handleWheel : function(e){
33552         var d = e.getWheelDelta();
33553         this.scroller.dom.scrollTop -= d*22;
33554         // set this here to prevent jumpy scrolling on large tables
33555         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33556         e.stopEvent();
33557     },
33558
33559     renderRows : function(startRow, endRow){
33560         // pull in all the crap needed to render rows
33561         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33562         var colCount = cm.getColumnCount();
33563
33564         if(ds.getCount() < 1){
33565             return ["", ""];
33566         }
33567
33568         // build a map for all the columns
33569         var cs = [];
33570         for(var i = 0; i < colCount; i++){
33571             var name = cm.getDataIndex(i);
33572             cs[i] = {
33573                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33574                 renderer : cm.getRenderer(i),
33575                 id : cm.getColumnId(i),
33576                 locked : cm.isLocked(i),
33577                 has_editor : cm.isCellEditable(i)
33578             };
33579         }
33580
33581         startRow = startRow || 0;
33582         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
33583
33584         // records to render
33585         var rs = ds.getRange(startRow, endRow);
33586
33587         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
33588     },
33589
33590     // As much as I hate to duplicate code, this was branched because FireFox really hates
33591     // [].join("") on strings. The performance difference was substantial enough to
33592     // branch this function
33593     doRender : Roo.isGecko ?
33594             function(cs, rs, ds, startRow, colCount, stripe){
33595                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33596                 // buffers
33597                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33598                 
33599                 var hasListener = this.grid.hasListener('rowclass');
33600                 var rowcfg = {};
33601                 for(var j = 0, len = rs.length; j < len; j++){
33602                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
33603                     for(var i = 0; i < colCount; i++){
33604                         c = cs[i];
33605                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33606                         p.id = c.id;
33607                         p.css = p.attr = "";
33608                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33609                         if(p.value == undefined || p.value === "") {
33610                             p.value = "&#160;";
33611                         }
33612                         if(c.has_editor){
33613                             p.css += ' x-grid-editable-cell';
33614                         }
33615                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
33616                             p.css +=  ' x-grid-dirty-cell';
33617                         }
33618                         var markup = ct.apply(p);
33619                         if(!c.locked){
33620                             cb+= markup;
33621                         }else{
33622                             lcb+= markup;
33623                         }
33624                     }
33625                     var alt = [];
33626                     if(stripe && ((rowIndex+1) % 2 == 0)){
33627                         alt.push("x-grid-row-alt")
33628                     }
33629                     if(r.dirty){
33630                         alt.push(  " x-grid-dirty-row");
33631                     }
33632                     rp.cells = lcb;
33633                     if(this.getRowClass){
33634                         alt.push(this.getRowClass(r, rowIndex));
33635                     }
33636                     if (hasListener) {
33637                         rowcfg = {
33638                              
33639                             record: r,
33640                             rowIndex : rowIndex,
33641                             rowClass : ''
33642                         };
33643                         this.grid.fireEvent('rowclass', this, rowcfg);
33644                         alt.push(rowcfg.rowClass);
33645                     }
33646                     rp.alt = alt.join(" ");
33647                     lbuf+= rt.apply(rp);
33648                     rp.cells = cb;
33649                     buf+=  rt.apply(rp);
33650                 }
33651                 return [lbuf, buf];
33652             } :
33653             function(cs, rs, ds, startRow, colCount, stripe){
33654                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33655                 // buffers
33656                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33657                 var hasListener = this.grid.hasListener('rowclass');
33658  
33659                 var rowcfg = {};
33660                 for(var j = 0, len = rs.length; j < len; j++){
33661                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
33662                     for(var i = 0; i < colCount; i++){
33663                         c = cs[i];
33664                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33665                         p.id = c.id;
33666                         p.css = p.attr = "";
33667                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33668                         if(p.value == undefined || p.value === "") {
33669                             p.value = "&#160;";
33670                         }
33671                         //Roo.log(c);
33672                          if(c.has_editor){
33673                             p.css += ' x-grid-editable-cell';
33674                         }
33675                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33676                             p.css += ' x-grid-dirty-cell' 
33677                         }
33678                         
33679                         var markup = ct.apply(p);
33680                         if(!c.locked){
33681                             cb[cb.length] = markup;
33682                         }else{
33683                             lcb[lcb.length] = markup;
33684                         }
33685                     }
33686                     var alt = [];
33687                     if(stripe && ((rowIndex+1) % 2 == 0)){
33688                         alt.push( "x-grid-row-alt");
33689                     }
33690                     if(r.dirty){
33691                         alt.push(" x-grid-dirty-row");
33692                     }
33693                     rp.cells = lcb;
33694                     if(this.getRowClass){
33695                         alt.push( this.getRowClass(r, rowIndex));
33696                     }
33697                     if (hasListener) {
33698                         rowcfg = {
33699                              
33700                             record: r,
33701                             rowIndex : rowIndex,
33702                             rowClass : ''
33703                         };
33704                         this.grid.fireEvent('rowclass', this, rowcfg);
33705                         alt.push(rowcfg.rowClass);
33706                     }
33707                     
33708                     rp.alt = alt.join(" ");
33709                     rp.cells = lcb.join("");
33710                     lbuf[lbuf.length] = rt.apply(rp);
33711                     rp.cells = cb.join("");
33712                     buf[buf.length] =  rt.apply(rp);
33713                 }
33714                 return [lbuf.join(""), buf.join("")];
33715             },
33716
33717     renderBody : function(){
33718         var markup = this.renderRows();
33719         var bt = this.templates.body;
33720         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
33721     },
33722
33723     /**
33724      * Refreshes the grid
33725      * @param {Boolean} headersToo
33726      */
33727     refresh : function(headersToo){
33728         this.fireEvent("beforerefresh", this);
33729         this.grid.stopEditing();
33730         var result = this.renderBody();
33731         this.lockedBody.update(result[0]);
33732         this.mainBody.update(result[1]);
33733         if(headersToo === true){
33734             this.updateHeaders();
33735             this.updateColumns();
33736             this.updateSplitters();
33737             this.updateHeaderSortState();
33738         }
33739         this.syncRowHeights();
33740         this.layout();
33741         this.fireEvent("refresh", this);
33742     },
33743
33744     handleColumnMove : function(cm, oldIndex, newIndex){
33745         this.indexMap = null;
33746         var s = this.getScrollState();
33747         this.refresh(true);
33748         this.restoreScroll(s);
33749         this.afterMove(newIndex);
33750     },
33751
33752     afterMove : function(colIndex){
33753         if(this.enableMoveAnim && Roo.enableFx){
33754             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
33755         }
33756         // if multisort - fix sortOrder, and reload..
33757         if (this.grid.dataSource.multiSort) {
33758             // the we can call sort again..
33759             var dm = this.grid.dataSource;
33760             var cm = this.grid.colModel;
33761             var so = [];
33762             for(var i = 0; i < cm.config.length; i++ ) {
33763                 
33764                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
33765                     continue; // dont' bother, it's not in sort list or being set.
33766                 }
33767                 
33768                 so.push(cm.config[i].dataIndex);
33769             };
33770             dm.sortOrder = so;
33771             dm.load(dm.lastOptions);
33772             
33773             
33774         }
33775         
33776     },
33777
33778     updateCell : function(dm, rowIndex, dataIndex){
33779         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
33780         if(typeof colIndex == "undefined"){ // not present in grid
33781             return;
33782         }
33783         var cm = this.grid.colModel;
33784         var cell = this.getCell(rowIndex, colIndex);
33785         var cellText = this.getCellText(rowIndex, colIndex);
33786
33787         var p = {
33788             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
33789             id : cm.getColumnId(colIndex),
33790             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
33791         };
33792         var renderer = cm.getRenderer(colIndex);
33793         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
33794         if(typeof val == "undefined" || val === "") {
33795             val = "&#160;";
33796         }
33797         cellText.innerHTML = val;
33798         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
33799         this.syncRowHeights(rowIndex, rowIndex);
33800     },
33801
33802     calcColumnWidth : function(colIndex, maxRowsToMeasure){
33803         var maxWidth = 0;
33804         if(this.grid.autoSizeHeaders){
33805             var h = this.getHeaderCellMeasure(colIndex);
33806             maxWidth = Math.max(maxWidth, h.scrollWidth);
33807         }
33808         var tb, index;
33809         if(this.cm.isLocked(colIndex)){
33810             tb = this.getLockedTable();
33811             index = colIndex;
33812         }else{
33813             tb = this.getBodyTable();
33814             index = colIndex - this.cm.getLockedCount();
33815         }
33816         if(tb && tb.rows){
33817             var rows = tb.rows;
33818             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
33819             for(var i = 0; i < stopIndex; i++){
33820                 var cell = rows[i].childNodes[index].firstChild;
33821                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
33822             }
33823         }
33824         return maxWidth + /*margin for error in IE*/ 5;
33825     },
33826     /**
33827      * Autofit a column to its content.
33828      * @param {Number} colIndex
33829      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
33830      */
33831      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
33832          if(this.cm.isHidden(colIndex)){
33833              return; // can't calc a hidden column
33834          }
33835         if(forceMinSize){
33836             var cid = this.cm.getColumnId(colIndex);
33837             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
33838            if(this.grid.autoSizeHeaders){
33839                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
33840            }
33841         }
33842         var newWidth = this.calcColumnWidth(colIndex);
33843         this.cm.setColumnWidth(colIndex,
33844             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
33845         if(!suppressEvent){
33846             this.grid.fireEvent("columnresize", colIndex, newWidth);
33847         }
33848     },
33849
33850     /**
33851      * Autofits all columns to their content and then expands to fit any extra space in the grid
33852      */
33853      autoSizeColumns : function(){
33854         var cm = this.grid.colModel;
33855         var colCount = cm.getColumnCount();
33856         for(var i = 0; i < colCount; i++){
33857             this.autoSizeColumn(i, true, true);
33858         }
33859         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
33860             this.fitColumns();
33861         }else{
33862             this.updateColumns();
33863             this.layout();
33864         }
33865     },
33866
33867     /**
33868      * Autofits all columns to the grid's width proportionate with their current size
33869      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
33870      */
33871     fitColumns : function(reserveScrollSpace){
33872         var cm = this.grid.colModel;
33873         var colCount = cm.getColumnCount();
33874         var cols = [];
33875         var width = 0;
33876         var i, w;
33877         for (i = 0; i < colCount; i++){
33878             if(!cm.isHidden(i) && !cm.isFixed(i)){
33879                 w = cm.getColumnWidth(i);
33880                 cols.push(i);
33881                 cols.push(w);
33882                 width += w;
33883             }
33884         }
33885         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
33886         if(reserveScrollSpace){
33887             avail -= 17;
33888         }
33889         var frac = (avail - cm.getTotalWidth())/width;
33890         while (cols.length){
33891             w = cols.pop();
33892             i = cols.pop();
33893             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
33894         }
33895         this.updateColumns();
33896         this.layout();
33897     },
33898
33899     onRowSelect : function(rowIndex){
33900         var row = this.getRowComposite(rowIndex);
33901         row.addClass("x-grid-row-selected");
33902     },
33903
33904     onRowDeselect : function(rowIndex){
33905         var row = this.getRowComposite(rowIndex);
33906         row.removeClass("x-grid-row-selected");
33907     },
33908
33909     onCellSelect : function(row, col){
33910         var cell = this.getCell(row, col);
33911         if(cell){
33912             Roo.fly(cell).addClass("x-grid-cell-selected");
33913         }
33914     },
33915
33916     onCellDeselect : function(row, col){
33917         var cell = this.getCell(row, col);
33918         if(cell){
33919             Roo.fly(cell).removeClass("x-grid-cell-selected");
33920         }
33921     },
33922
33923     updateHeaderSortState : function(){
33924         
33925         // sort state can be single { field: xxx, direction : yyy}
33926         // or   { xxx=>ASC , yyy : DESC ..... }
33927         
33928         var mstate = {};
33929         if (!this.ds.multiSort) { 
33930             var state = this.ds.getSortState();
33931             if(!state){
33932                 return;
33933             }
33934             mstate[state.field] = state.direction;
33935             // FIXME... - this is not used here.. but might be elsewhere..
33936             this.sortState = state;
33937             
33938         } else {
33939             mstate = this.ds.sortToggle;
33940         }
33941         //remove existing sort classes..
33942         
33943         var sc = this.sortClasses;
33944         var hds = this.el.select(this.headerSelector).removeClass(sc);
33945         
33946         for(var f in mstate) {
33947         
33948             var sortColumn = this.cm.findColumnIndex(f);
33949             
33950             if(sortColumn != -1){
33951                 var sortDir = mstate[f];        
33952                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
33953             }
33954         }
33955         
33956          
33957         
33958     },
33959
33960
33961     handleHeaderClick : function(g, index,e){
33962         
33963         Roo.log("header click");
33964         
33965         if (Roo.isTouch) {
33966             // touch events on header are handled by context
33967             this.handleHdCtx(g,index,e);
33968             return;
33969         }
33970         
33971         
33972         if(this.headersDisabled){
33973             return;
33974         }
33975         var dm = g.dataSource, cm = g.colModel;
33976         if(!cm.isSortable(index)){
33977             return;
33978         }
33979         g.stopEditing();
33980         
33981         if (dm.multiSort) {
33982             // update the sortOrder
33983             var so = [];
33984             for(var i = 0; i < cm.config.length; i++ ) {
33985                 
33986                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
33987                     continue; // dont' bother, it's not in sort list or being set.
33988                 }
33989                 
33990                 so.push(cm.config[i].dataIndex);
33991             };
33992             dm.sortOrder = so;
33993         }
33994         
33995         
33996         dm.sort(cm.getDataIndex(index));
33997     },
33998
33999
34000     destroy : function(){
34001         if(this.colMenu){
34002             this.colMenu.removeAll();
34003             Roo.menu.MenuMgr.unregister(this.colMenu);
34004             this.colMenu.getEl().remove();
34005             delete this.colMenu;
34006         }
34007         if(this.hmenu){
34008             this.hmenu.removeAll();
34009             Roo.menu.MenuMgr.unregister(this.hmenu);
34010             this.hmenu.getEl().remove();
34011             delete this.hmenu;
34012         }
34013         if(this.grid.enableColumnMove){
34014             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34015             if(dds){
34016                 for(var dd in dds){
34017                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34018                         var elid = dds[dd].dragElId;
34019                         dds[dd].unreg();
34020                         Roo.get(elid).remove();
34021                     } else if(dds[dd].config.isTarget){
34022                         dds[dd].proxyTop.remove();
34023                         dds[dd].proxyBottom.remove();
34024                         dds[dd].unreg();
34025                     }
34026                     if(Roo.dd.DDM.locationCache[dd]){
34027                         delete Roo.dd.DDM.locationCache[dd];
34028                     }
34029                 }
34030                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34031             }
34032         }
34033         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34034         this.bind(null, null);
34035         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34036     },
34037
34038     handleLockChange : function(){
34039         this.refresh(true);
34040     },
34041
34042     onDenyColumnLock : function(){
34043
34044     },
34045
34046     onDenyColumnHide : function(){
34047
34048     },
34049
34050     handleHdMenuClick : function(item){
34051         var index = this.hdCtxIndex;
34052         var cm = this.cm, ds = this.ds;
34053         switch(item.id){
34054             case "asc":
34055                 ds.sort(cm.getDataIndex(index), "ASC");
34056                 break;
34057             case "desc":
34058                 ds.sort(cm.getDataIndex(index), "DESC");
34059                 break;
34060             case "lock":
34061                 var lc = cm.getLockedCount();
34062                 if(cm.getColumnCount(true) <= lc+1){
34063                     this.onDenyColumnLock();
34064                     return;
34065                 }
34066                 if(lc != index){
34067                     cm.setLocked(index, true, true);
34068                     cm.moveColumn(index, lc);
34069                     this.grid.fireEvent("columnmove", index, lc);
34070                 }else{
34071                     cm.setLocked(index, true);
34072                 }
34073             break;
34074             case "unlock":
34075                 var lc = cm.getLockedCount();
34076                 if((lc-1) != index){
34077                     cm.setLocked(index, false, true);
34078                     cm.moveColumn(index, lc-1);
34079                     this.grid.fireEvent("columnmove", index, lc-1);
34080                 }else{
34081                     cm.setLocked(index, false);
34082                 }
34083             break;
34084             case 'wider': // used to expand cols on touch..
34085             case 'narrow':
34086                 var cw = cm.getColumnWidth(index);
34087                 cw += (item.id == 'wider' ? 1 : -1) * 50;
34088                 cw = Math.max(0, cw);
34089                 cw = Math.min(cw,4000);
34090                 cm.setColumnWidth(index, cw);
34091                 break;
34092                 
34093             default:
34094                 index = cm.getIndexById(item.id.substr(4));
34095                 if(index != -1){
34096                     if(item.checked && cm.getColumnCount(true) <= 1){
34097                         this.onDenyColumnHide();
34098                         return false;
34099                     }
34100                     cm.setHidden(index, item.checked);
34101                 }
34102         }
34103         return true;
34104     },
34105
34106     beforeColMenuShow : function(){
34107         var cm = this.cm,  colCount = cm.getColumnCount();
34108         this.colMenu.removeAll();
34109         for(var i = 0; i < colCount; i++){
34110             this.colMenu.add(new Roo.menu.CheckItem({
34111                 id: "col-"+cm.getColumnId(i),
34112                 text: cm.getColumnHeader(i),
34113                 checked: !cm.isHidden(i),
34114                 hideOnClick:false
34115             }));
34116         }
34117     },
34118
34119     handleHdCtx : function(g, index, e){
34120         e.stopEvent();
34121         var hd = this.getHeaderCell(index);
34122         this.hdCtxIndex = index;
34123         var ms = this.hmenu.items, cm = this.cm;
34124         ms.get("asc").setDisabled(!cm.isSortable(index));
34125         ms.get("desc").setDisabled(!cm.isSortable(index));
34126         if(this.grid.enableColLock !== false){
34127             ms.get("lock").setDisabled(cm.isLocked(index));
34128             ms.get("unlock").setDisabled(!cm.isLocked(index));
34129         }
34130         this.hmenu.show(hd, "tl-bl");
34131     },
34132
34133     handleHdOver : function(e){
34134         var hd = this.findHeaderCell(e.getTarget());
34135         if(hd && !this.headersDisabled){
34136             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34137                this.fly(hd).addClass("x-grid-hd-over");
34138             }
34139         }
34140     },
34141
34142     handleHdOut : function(e){
34143         var hd = this.findHeaderCell(e.getTarget());
34144         if(hd){
34145             this.fly(hd).removeClass("x-grid-hd-over");
34146         }
34147     },
34148
34149     handleSplitDblClick : function(e, t){
34150         var i = this.getCellIndex(t);
34151         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34152             this.autoSizeColumn(i, true);
34153             this.layout();
34154         }
34155     },
34156
34157     render : function(){
34158
34159         var cm = this.cm;
34160         var colCount = cm.getColumnCount();
34161
34162         if(this.grid.monitorWindowResize === true){
34163             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34164         }
34165         var header = this.renderHeaders();
34166         var body = this.templates.body.apply({rows:""});
34167         var html = this.templates.master.apply({
34168             lockedBody: body,
34169             body: body,
34170             lockedHeader: header[0],
34171             header: header[1]
34172         });
34173
34174         //this.updateColumns();
34175
34176         this.grid.getGridEl().dom.innerHTML = html;
34177
34178         this.initElements();
34179         
34180         // a kludge to fix the random scolling effect in webkit
34181         this.el.on("scroll", function() {
34182             this.el.dom.scrollTop=0; // hopefully not recursive..
34183         },this);
34184
34185         this.scroller.on("scroll", this.handleScroll, this);
34186         this.lockedBody.on("mousewheel", this.handleWheel, this);
34187         this.mainBody.on("mousewheel", this.handleWheel, this);
34188
34189         this.mainHd.on("mouseover", this.handleHdOver, this);
34190         this.mainHd.on("mouseout", this.handleHdOut, this);
34191         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34192                 {delegate: "."+this.splitClass});
34193
34194         this.lockedHd.on("mouseover", this.handleHdOver, this);
34195         this.lockedHd.on("mouseout", this.handleHdOut, this);
34196         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34197                 {delegate: "."+this.splitClass});
34198
34199         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34200             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34201         }
34202
34203         this.updateSplitters();
34204
34205         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34206             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34207             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34208         }
34209
34210         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34211             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34212             this.hmenu.add(
34213                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34214                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34215             );
34216             if(this.grid.enableColLock !== false){
34217                 this.hmenu.add('-',
34218                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34219                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34220                 );
34221             }
34222             if (Roo.isTouch) {
34223                  this.hmenu.add('-',
34224                     {id:"wider", text: this.columnsWiderText},
34225                     {id:"narrow", text: this.columnsNarrowText }
34226                 );
34227                 
34228                  
34229             }
34230             
34231             if(this.grid.enableColumnHide !== false){
34232
34233                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34234                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34235                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34236
34237                 this.hmenu.add('-',
34238                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34239                 );
34240             }
34241             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34242
34243             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34244         }
34245
34246         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34247             this.dd = new Roo.grid.GridDragZone(this.grid, {
34248                 ddGroup : this.grid.ddGroup || 'GridDD'
34249             });
34250             
34251         }
34252
34253         /*
34254         for(var i = 0; i < colCount; i++){
34255             if(cm.isHidden(i)){
34256                 this.hideColumn(i);
34257             }
34258             if(cm.config[i].align){
34259                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34260                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34261             }
34262         }*/
34263         
34264         this.updateHeaderSortState();
34265
34266         this.beforeInitialResize();
34267         this.layout(true);
34268
34269         // two part rendering gives faster view to the user
34270         this.renderPhase2.defer(1, this);
34271     },
34272
34273     renderPhase2 : function(){
34274         // render the rows now
34275         this.refresh();
34276         if(this.grid.autoSizeColumns){
34277             this.autoSizeColumns();
34278         }
34279     },
34280
34281     beforeInitialResize : function(){
34282
34283     },
34284
34285     onColumnSplitterMoved : function(i, w){
34286         this.userResized = true;
34287         var cm = this.grid.colModel;
34288         cm.setColumnWidth(i, w, true);
34289         var cid = cm.getColumnId(i);
34290         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34291         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34292         this.updateSplitters();
34293         this.layout();
34294         this.grid.fireEvent("columnresize", i, w);
34295     },
34296
34297     syncRowHeights : function(startIndex, endIndex){
34298         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34299             startIndex = startIndex || 0;
34300             var mrows = this.getBodyTable().rows;
34301             var lrows = this.getLockedTable().rows;
34302             var len = mrows.length-1;
34303             endIndex = Math.min(endIndex || len, len);
34304             for(var i = startIndex; i <= endIndex; i++){
34305                 var m = mrows[i], l = lrows[i];
34306                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34307                 m.style.height = l.style.height = h + "px";
34308             }
34309         }
34310     },
34311
34312     layout : function(initialRender, is2ndPass){
34313         var g = this.grid;
34314         var auto = g.autoHeight;
34315         var scrollOffset = 16;
34316         var c = g.getGridEl(), cm = this.cm,
34317                 expandCol = g.autoExpandColumn,
34318                 gv = this;
34319         //c.beginMeasure();
34320
34321         if(!c.dom.offsetWidth){ // display:none?
34322             if(initialRender){
34323                 this.lockedWrap.show();
34324                 this.mainWrap.show();
34325             }
34326             return;
34327         }
34328
34329         var hasLock = this.cm.isLocked(0);
34330
34331         var tbh = this.headerPanel.getHeight();
34332         var bbh = this.footerPanel.getHeight();
34333
34334         if(auto){
34335             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34336             var newHeight = ch + c.getBorderWidth("tb");
34337             if(g.maxHeight){
34338                 newHeight = Math.min(g.maxHeight, newHeight);
34339             }
34340             c.setHeight(newHeight);
34341         }
34342
34343         if(g.autoWidth){
34344             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34345         }
34346
34347         var s = this.scroller;
34348
34349         var csize = c.getSize(true);
34350
34351         this.el.setSize(csize.width, csize.height);
34352
34353         this.headerPanel.setWidth(csize.width);
34354         this.footerPanel.setWidth(csize.width);
34355
34356         var hdHeight = this.mainHd.getHeight();
34357         var vw = csize.width;
34358         var vh = csize.height - (tbh + bbh);
34359
34360         s.setSize(vw, vh);
34361
34362         var bt = this.getBodyTable();
34363         
34364         if(cm.getLockedCount() == cm.config.length){
34365             bt = this.getLockedTable();
34366         }
34367         
34368         var ltWidth = hasLock ?
34369                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34370
34371         var scrollHeight = bt.offsetHeight;
34372         var scrollWidth = ltWidth + bt.offsetWidth;
34373         var vscroll = false, hscroll = false;
34374
34375         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34376
34377         var lw = this.lockedWrap, mw = this.mainWrap;
34378         var lb = this.lockedBody, mb = this.mainBody;
34379
34380         setTimeout(function(){
34381             var t = s.dom.offsetTop;
34382             var w = s.dom.clientWidth,
34383                 h = s.dom.clientHeight;
34384
34385             lw.setTop(t);
34386             lw.setSize(ltWidth, h);
34387
34388             mw.setLeftTop(ltWidth, t);
34389             mw.setSize(w-ltWidth, h);
34390
34391             lb.setHeight(h-hdHeight);
34392             mb.setHeight(h-hdHeight);
34393
34394             if(is2ndPass !== true && !gv.userResized && expandCol){
34395                 // high speed resize without full column calculation
34396                 
34397                 var ci = cm.getIndexById(expandCol);
34398                 if (ci < 0) {
34399                     ci = cm.findColumnIndex(expandCol);
34400                 }
34401                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34402                 var expandId = cm.getColumnId(ci);
34403                 var  tw = cm.getTotalWidth(false);
34404                 var currentWidth = cm.getColumnWidth(ci);
34405                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34406                 if(currentWidth != cw){
34407                     cm.setColumnWidth(ci, cw, true);
34408                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34409                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34410                     gv.updateSplitters();
34411                     gv.layout(false, true);
34412                 }
34413             }
34414
34415             if(initialRender){
34416                 lw.show();
34417                 mw.show();
34418             }
34419             //c.endMeasure();
34420         }, 10);
34421     },
34422
34423     onWindowResize : function(){
34424         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34425             return;
34426         }
34427         this.layout();
34428     },
34429
34430     appendFooter : function(parentEl){
34431         return null;
34432     },
34433
34434     sortAscText : "Sort Ascending",
34435     sortDescText : "Sort Descending",
34436     lockText : "Lock Column",
34437     unlockText : "Unlock Column",
34438     columnsText : "Columns",
34439  
34440     columnsWiderText : "Wider",
34441     columnsNarrowText : "Thinner"
34442 });
34443
34444
34445 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34446     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34447     this.proxy.el.addClass('x-grid3-col-dd');
34448 };
34449
34450 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34451     handleMouseDown : function(e){
34452
34453     },
34454
34455     callHandleMouseDown : function(e){
34456         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34457     }
34458 });
34459 /*
34460  * Based on:
34461  * Ext JS Library 1.1.1
34462  * Copyright(c) 2006-2007, Ext JS, LLC.
34463  *
34464  * Originally Released Under LGPL - original licence link has changed is not relivant.
34465  *
34466  * Fork - LGPL
34467  * <script type="text/javascript">
34468  */
34469  
34470 // private
34471 // This is a support class used internally by the Grid components
34472 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34473     this.grid = grid;
34474     this.view = grid.getView();
34475     this.proxy = this.view.resizeProxy;
34476     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34477         "gridSplitters" + this.grid.getGridEl().id, {
34478         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34479     });
34480     this.setHandleElId(Roo.id(hd));
34481     this.setOuterHandleElId(Roo.id(hd2));
34482     this.scroll = false;
34483 };
34484 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34485     fly: Roo.Element.fly,
34486
34487     b4StartDrag : function(x, y){
34488         this.view.headersDisabled = true;
34489         this.proxy.setHeight(this.view.mainWrap.getHeight());
34490         var w = this.cm.getColumnWidth(this.cellIndex);
34491         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34492         this.resetConstraints();
34493         this.setXConstraint(minw, 1000);
34494         this.setYConstraint(0, 0);
34495         this.minX = x - minw;
34496         this.maxX = x + 1000;
34497         this.startPos = x;
34498         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34499     },
34500
34501
34502     handleMouseDown : function(e){
34503         ev = Roo.EventObject.setEvent(e);
34504         var t = this.fly(ev.getTarget());
34505         if(t.hasClass("x-grid-split")){
34506             this.cellIndex = this.view.getCellIndex(t.dom);
34507             this.split = t.dom;
34508             this.cm = this.grid.colModel;
34509             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34510                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34511             }
34512         }
34513     },
34514
34515     endDrag : function(e){
34516         this.view.headersDisabled = false;
34517         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34518         var diff = endX - this.startPos;
34519         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34520     },
34521
34522     autoOffset : function(){
34523         this.setDelta(0,0);
34524     }
34525 });/*
34526  * Based on:
34527  * Ext JS Library 1.1.1
34528  * Copyright(c) 2006-2007, Ext JS, LLC.
34529  *
34530  * Originally Released Under LGPL - original licence link has changed is not relivant.
34531  *
34532  * Fork - LGPL
34533  * <script type="text/javascript">
34534  */
34535  
34536 // private
34537 // This is a support class used internally by the Grid components
34538 Roo.grid.GridDragZone = function(grid, config){
34539     this.view = grid.getView();
34540     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34541     if(this.view.lockedBody){
34542         this.setHandleElId(Roo.id(this.view.mainBody.dom));
34543         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
34544     }
34545     this.scroll = false;
34546     this.grid = grid;
34547     this.ddel = document.createElement('div');
34548     this.ddel.className = 'x-grid-dd-wrap';
34549 };
34550
34551 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
34552     ddGroup : "GridDD",
34553
34554     getDragData : function(e){
34555         var t = Roo.lib.Event.getTarget(e);
34556         var rowIndex = this.view.findRowIndex(t);
34557         var sm = this.grid.selModel;
34558             
34559         //Roo.log(rowIndex);
34560         
34561         if (sm.getSelectedCell) {
34562             // cell selection..
34563             if (!sm.getSelectedCell()) {
34564                 return false;
34565             }
34566             if (rowIndex != sm.getSelectedCell()[0]) {
34567                 return false;
34568             }
34569         
34570         }
34571         
34572         if(rowIndex !== false){
34573             
34574             // if editorgrid.. 
34575             
34576             
34577             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
34578                
34579             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
34580               //  
34581             //}
34582             if (e.hasModifier()){
34583                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
34584             }
34585             
34586             Roo.log("getDragData");
34587             
34588             return {
34589                 grid: this.grid,
34590                 ddel: this.ddel,
34591                 rowIndex: rowIndex,
34592                 selections:sm.getSelections ? sm.getSelections() : (
34593                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
34594                 )
34595             };
34596         }
34597         return false;
34598     },
34599
34600     onInitDrag : function(e){
34601         var data = this.dragData;
34602         this.ddel.innerHTML = this.grid.getDragDropText();
34603         this.proxy.update(this.ddel);
34604         // fire start drag?
34605     },
34606
34607     afterRepair : function(){
34608         this.dragging = false;
34609     },
34610
34611     getRepairXY : function(e, data){
34612         return false;
34613     },
34614
34615     onEndDrag : function(data, e){
34616         // fire end drag?
34617     },
34618
34619     onValidDrop : function(dd, e, id){
34620         // fire drag drop?
34621         this.hideProxy();
34622     },
34623
34624     beforeInvalidDrop : function(e, id){
34625
34626     }
34627 });/*
34628  * Based on:
34629  * Ext JS Library 1.1.1
34630  * Copyright(c) 2006-2007, Ext JS, LLC.
34631  *
34632  * Originally Released Under LGPL - original licence link has changed is not relivant.
34633  *
34634  * Fork - LGPL
34635  * <script type="text/javascript">
34636  */
34637  
34638
34639 /**
34640  * @class Roo.grid.ColumnModel
34641  * @extends Roo.util.Observable
34642  * This is the default implementation of a ColumnModel used by the Grid. It defines
34643  * the columns in the grid.
34644  * <br>Usage:<br>
34645  <pre><code>
34646  var colModel = new Roo.grid.ColumnModel([
34647         {header: "Ticker", width: 60, sortable: true, locked: true},
34648         {header: "Company Name", width: 150, sortable: true},
34649         {header: "Market Cap.", width: 100, sortable: true},
34650         {header: "$ Sales", width: 100, sortable: true, renderer: money},
34651         {header: "Employees", width: 100, sortable: true, resizable: false}
34652  ]);
34653  </code></pre>
34654  * <p>
34655  
34656  * The config options listed for this class are options which may appear in each
34657  * individual column definition.
34658  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
34659  * @constructor
34660  * @param {Object} config An Array of column config objects. See this class's
34661  * config objects for details.
34662 */
34663 Roo.grid.ColumnModel = function(config){
34664         /**
34665      * The config passed into the constructor
34666      */
34667     this.config = config;
34668     this.lookup = {};
34669
34670     // if no id, create one
34671     // if the column does not have a dataIndex mapping,
34672     // map it to the order it is in the config
34673     for(var i = 0, len = config.length; i < len; i++){
34674         var c = config[i];
34675         if(typeof c.dataIndex == "undefined"){
34676             c.dataIndex = i;
34677         }
34678         if(typeof c.renderer == "string"){
34679             c.renderer = Roo.util.Format[c.renderer];
34680         }
34681         if(typeof c.id == "undefined"){
34682             c.id = Roo.id();
34683         }
34684         if(c.editor && c.editor.xtype){
34685             c.editor  = Roo.factory(c.editor, Roo.grid);
34686         }
34687         if(c.editor && c.editor.isFormField){
34688             c.editor = new Roo.grid.GridEditor(c.editor);
34689         }
34690         this.lookup[c.id] = c;
34691     }
34692
34693     /**
34694      * The width of columns which have no width specified (defaults to 100)
34695      * @type Number
34696      */
34697     this.defaultWidth = 100;
34698
34699     /**
34700      * Default sortable of columns which have no sortable specified (defaults to false)
34701      * @type Boolean
34702      */
34703     this.defaultSortable = false;
34704
34705     this.addEvents({
34706         /**
34707              * @event widthchange
34708              * Fires when the width of a column changes.
34709              * @param {ColumnModel} this
34710              * @param {Number} columnIndex The column index
34711              * @param {Number} newWidth The new width
34712              */
34713             "widthchange": true,
34714         /**
34715              * @event headerchange
34716              * Fires when the text of a header changes.
34717              * @param {ColumnModel} this
34718              * @param {Number} columnIndex The column index
34719              * @param {Number} newText The new header text
34720              */
34721             "headerchange": true,
34722         /**
34723              * @event hiddenchange
34724              * Fires when a column is hidden or "unhidden".
34725              * @param {ColumnModel} this
34726              * @param {Number} columnIndex The column index
34727              * @param {Boolean} hidden true if hidden, false otherwise
34728              */
34729             "hiddenchange": true,
34730             /**
34731          * @event columnmoved
34732          * Fires when a column is moved.
34733          * @param {ColumnModel} this
34734          * @param {Number} oldIndex
34735          * @param {Number} newIndex
34736          */
34737         "columnmoved" : true,
34738         /**
34739          * @event columlockchange
34740          * Fires when a column's locked state is changed
34741          * @param {ColumnModel} this
34742          * @param {Number} colIndex
34743          * @param {Boolean} locked true if locked
34744          */
34745         "columnlockchange" : true
34746     });
34747     Roo.grid.ColumnModel.superclass.constructor.call(this);
34748 };
34749 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
34750     /**
34751      * @cfg {String} header The header text to display in the Grid view.
34752      */
34753     /**
34754      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
34755      * {@link Roo.data.Record} definition from which to draw the column's value. If not
34756      * specified, the column's index is used as an index into the Record's data Array.
34757      */
34758     /**
34759      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
34760      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
34761      */
34762     /**
34763      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
34764      * Defaults to the value of the {@link #defaultSortable} property.
34765      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
34766      */
34767     /**
34768      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
34769      */
34770     /**
34771      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
34772      */
34773     /**
34774      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
34775      */
34776     /**
34777      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
34778      */
34779     /**
34780      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
34781      * given the cell's data value. See {@link #setRenderer}. If not specified, the
34782      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
34783      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
34784      */
34785        /**
34786      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
34787      */
34788     /**
34789      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
34790      */
34791     /**
34792      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
34793      */
34794     /**
34795      * @cfg {String} cursor (Optional)
34796      */
34797     /**
34798      * @cfg {String} tooltip (Optional)
34799      */
34800     /**
34801      * @cfg {Number} xs (Optional)
34802      */
34803     /**
34804      * @cfg {Number} sm (Optional)
34805      */
34806     /**
34807      * @cfg {Number} md (Optional)
34808      */
34809     /**
34810      * @cfg {Number} lg (Optional)
34811      */
34812     /**
34813      * Returns the id of the column at the specified index.
34814      * @param {Number} index The column index
34815      * @return {String} the id
34816      */
34817     getColumnId : function(index){
34818         return this.config[index].id;
34819     },
34820
34821     /**
34822      * Returns the column for a specified id.
34823      * @param {String} id The column id
34824      * @return {Object} the column
34825      */
34826     getColumnById : function(id){
34827         return this.lookup[id];
34828     },
34829
34830     
34831     /**
34832      * Returns the column for a specified dataIndex.
34833      * @param {String} dataIndex The column dataIndex
34834      * @return {Object|Boolean} the column or false if not found
34835      */
34836     getColumnByDataIndex: function(dataIndex){
34837         var index = this.findColumnIndex(dataIndex);
34838         return index > -1 ? this.config[index] : false;
34839     },
34840     
34841     /**
34842      * Returns the index for a specified column id.
34843      * @param {String} id The column id
34844      * @return {Number} the index, or -1 if not found
34845      */
34846     getIndexById : function(id){
34847         for(var i = 0, len = this.config.length; i < len; i++){
34848             if(this.config[i].id == id){
34849                 return i;
34850             }
34851         }
34852         return -1;
34853     },
34854     
34855     /**
34856      * Returns the index for a specified column dataIndex.
34857      * @param {String} dataIndex The column dataIndex
34858      * @return {Number} the index, or -1 if not found
34859      */
34860     
34861     findColumnIndex : function(dataIndex){
34862         for(var i = 0, len = this.config.length; i < len; i++){
34863             if(this.config[i].dataIndex == dataIndex){
34864                 return i;
34865             }
34866         }
34867         return -1;
34868     },
34869     
34870     
34871     moveColumn : function(oldIndex, newIndex){
34872         var c = this.config[oldIndex];
34873         this.config.splice(oldIndex, 1);
34874         this.config.splice(newIndex, 0, c);
34875         this.dataMap = null;
34876         this.fireEvent("columnmoved", this, oldIndex, newIndex);
34877     },
34878
34879     isLocked : function(colIndex){
34880         return this.config[colIndex].locked === true;
34881     },
34882
34883     setLocked : function(colIndex, value, suppressEvent){
34884         if(this.isLocked(colIndex) == value){
34885             return;
34886         }
34887         this.config[colIndex].locked = value;
34888         if(!suppressEvent){
34889             this.fireEvent("columnlockchange", this, colIndex, value);
34890         }
34891     },
34892
34893     getTotalLockedWidth : function(){
34894         var totalWidth = 0;
34895         for(var i = 0; i < this.config.length; i++){
34896             if(this.isLocked(i) && !this.isHidden(i)){
34897                 this.totalWidth += this.getColumnWidth(i);
34898             }
34899         }
34900         return totalWidth;
34901     },
34902
34903     getLockedCount : function(){
34904         for(var i = 0, len = this.config.length; i < len; i++){
34905             if(!this.isLocked(i)){
34906                 return i;
34907             }
34908         }
34909         
34910         return this.config.length;
34911     },
34912
34913     /**
34914      * Returns the number of columns.
34915      * @return {Number}
34916      */
34917     getColumnCount : function(visibleOnly){
34918         if(visibleOnly === true){
34919             var c = 0;
34920             for(var i = 0, len = this.config.length; i < len; i++){
34921                 if(!this.isHidden(i)){
34922                     c++;
34923                 }
34924             }
34925             return c;
34926         }
34927         return this.config.length;
34928     },
34929
34930     /**
34931      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
34932      * @param {Function} fn
34933      * @param {Object} scope (optional)
34934      * @return {Array} result
34935      */
34936     getColumnsBy : function(fn, scope){
34937         var r = [];
34938         for(var i = 0, len = this.config.length; i < len; i++){
34939             var c = this.config[i];
34940             if(fn.call(scope||this, c, i) === true){
34941                 r[r.length] = c;
34942             }
34943         }
34944         return r;
34945     },
34946
34947     /**
34948      * Returns true if the specified column is sortable.
34949      * @param {Number} col The column index
34950      * @return {Boolean}
34951      */
34952     isSortable : function(col){
34953         if(typeof this.config[col].sortable == "undefined"){
34954             return this.defaultSortable;
34955         }
34956         return this.config[col].sortable;
34957     },
34958
34959     /**
34960      * Returns the rendering (formatting) function defined for the column.
34961      * @param {Number} col The column index.
34962      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
34963      */
34964     getRenderer : function(col){
34965         if(!this.config[col].renderer){
34966             return Roo.grid.ColumnModel.defaultRenderer;
34967         }
34968         return this.config[col].renderer;
34969     },
34970
34971     /**
34972      * Sets the rendering (formatting) function for a column.
34973      * @param {Number} col The column index
34974      * @param {Function} fn The function to use to process the cell's raw data
34975      * to return HTML markup for the grid view. The render function is called with
34976      * the following parameters:<ul>
34977      * <li>Data value.</li>
34978      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
34979      * <li>css A CSS style string to apply to the table cell.</li>
34980      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
34981      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
34982      * <li>Row index</li>
34983      * <li>Column index</li>
34984      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
34985      */
34986     setRenderer : function(col, fn){
34987         this.config[col].renderer = fn;
34988     },
34989
34990     /**
34991      * Returns the width for the specified column.
34992      * @param {Number} col The column index
34993      * @return {Number}
34994      */
34995     getColumnWidth : function(col){
34996         return this.config[col].width * 1 || this.defaultWidth;
34997     },
34998
34999     /**
35000      * Sets the width for a column.
35001      * @param {Number} col The column index
35002      * @param {Number} width The new width
35003      */
35004     setColumnWidth : function(col, width, suppressEvent){
35005         this.config[col].width = width;
35006         this.totalWidth = null;
35007         if(!suppressEvent){
35008              this.fireEvent("widthchange", this, col, width);
35009         }
35010     },
35011
35012     /**
35013      * Returns the total width of all columns.
35014      * @param {Boolean} includeHidden True to include hidden column widths
35015      * @return {Number}
35016      */
35017     getTotalWidth : function(includeHidden){
35018         if(!this.totalWidth){
35019             this.totalWidth = 0;
35020             for(var i = 0, len = this.config.length; i < len; i++){
35021                 if(includeHidden || !this.isHidden(i)){
35022                     this.totalWidth += this.getColumnWidth(i);
35023                 }
35024             }
35025         }
35026         return this.totalWidth;
35027     },
35028
35029     /**
35030      * Returns the header for the specified column.
35031      * @param {Number} col The column index
35032      * @return {String}
35033      */
35034     getColumnHeader : function(col){
35035         return this.config[col].header;
35036     },
35037
35038     /**
35039      * Sets the header for a column.
35040      * @param {Number} col The column index
35041      * @param {String} header The new header
35042      */
35043     setColumnHeader : function(col, header){
35044         this.config[col].header = header;
35045         this.fireEvent("headerchange", this, col, header);
35046     },
35047
35048     /**
35049      * Returns the tooltip for the specified column.
35050      * @param {Number} col The column index
35051      * @return {String}
35052      */
35053     getColumnTooltip : function(col){
35054             return this.config[col].tooltip;
35055     },
35056     /**
35057      * Sets the tooltip for a column.
35058      * @param {Number} col The column index
35059      * @param {String} tooltip The new tooltip
35060      */
35061     setColumnTooltip : function(col, tooltip){
35062             this.config[col].tooltip = tooltip;
35063     },
35064
35065     /**
35066      * Returns the dataIndex for the specified column.
35067      * @param {Number} col The column index
35068      * @return {Number}
35069      */
35070     getDataIndex : function(col){
35071         return this.config[col].dataIndex;
35072     },
35073
35074     /**
35075      * Sets the dataIndex for a column.
35076      * @param {Number} col The column index
35077      * @param {Number} dataIndex The new dataIndex
35078      */
35079     setDataIndex : function(col, dataIndex){
35080         this.config[col].dataIndex = dataIndex;
35081     },
35082
35083     
35084     
35085     /**
35086      * Returns true if the cell is editable.
35087      * @param {Number} colIndex The column index
35088      * @param {Number} rowIndex The row index - this is nto actually used..?
35089      * @return {Boolean}
35090      */
35091     isCellEditable : function(colIndex, rowIndex){
35092         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35093     },
35094
35095     /**
35096      * Returns the editor defined for the cell/column.
35097      * return false or null to disable editing.
35098      * @param {Number} colIndex The column index
35099      * @param {Number} rowIndex The row index
35100      * @return {Object}
35101      */
35102     getCellEditor : function(colIndex, rowIndex){
35103         return this.config[colIndex].editor;
35104     },
35105
35106     /**
35107      * Sets if a column is editable.
35108      * @param {Number} col The column index
35109      * @param {Boolean} editable True if the column is editable
35110      */
35111     setEditable : function(col, editable){
35112         this.config[col].editable = editable;
35113     },
35114
35115
35116     /**
35117      * Returns true if the column is hidden.
35118      * @param {Number} colIndex The column index
35119      * @return {Boolean}
35120      */
35121     isHidden : function(colIndex){
35122         return this.config[colIndex].hidden;
35123     },
35124
35125
35126     /**
35127      * Returns true if the column width cannot be changed
35128      */
35129     isFixed : function(colIndex){
35130         return this.config[colIndex].fixed;
35131     },
35132
35133     /**
35134      * Returns true if the column can be resized
35135      * @return {Boolean}
35136      */
35137     isResizable : function(colIndex){
35138         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35139     },
35140     /**
35141      * Sets if a column is hidden.
35142      * @param {Number} colIndex The column index
35143      * @param {Boolean} hidden True if the column is hidden
35144      */
35145     setHidden : function(colIndex, hidden){
35146         this.config[colIndex].hidden = hidden;
35147         this.totalWidth = null;
35148         this.fireEvent("hiddenchange", this, colIndex, hidden);
35149     },
35150
35151     /**
35152      * Sets the editor for a column.
35153      * @param {Number} col The column index
35154      * @param {Object} editor The editor object
35155      */
35156     setEditor : function(col, editor){
35157         this.config[col].editor = editor;
35158     }
35159 });
35160
35161 Roo.grid.ColumnModel.defaultRenderer = function(value)
35162 {
35163     if(typeof value == "object") {
35164         return value;
35165     }
35166         if(typeof value == "string" && value.length < 1){
35167             return "&#160;";
35168         }
35169     
35170         return String.format("{0}", value);
35171 };
35172
35173 // Alias for backwards compatibility
35174 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35175 /*
35176  * Based on:
35177  * Ext JS Library 1.1.1
35178  * Copyright(c) 2006-2007, Ext JS, LLC.
35179  *
35180  * Originally Released Under LGPL - original licence link has changed is not relivant.
35181  *
35182  * Fork - LGPL
35183  * <script type="text/javascript">
35184  */
35185
35186 /**
35187  * @class Roo.grid.AbstractSelectionModel
35188  * @extends Roo.util.Observable
35189  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35190  * implemented by descendant classes.  This class should not be directly instantiated.
35191  * @constructor
35192  */
35193 Roo.grid.AbstractSelectionModel = function(){
35194     this.locked = false;
35195     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35196 };
35197
35198 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35199     /** @ignore Called by the grid automatically. Do not call directly. */
35200     init : function(grid){
35201         this.grid = grid;
35202         this.initEvents();
35203     },
35204
35205     /**
35206      * Locks the selections.
35207      */
35208     lock : function(){
35209         this.locked = true;
35210     },
35211
35212     /**
35213      * Unlocks the selections.
35214      */
35215     unlock : function(){
35216         this.locked = false;
35217     },
35218
35219     /**
35220      * Returns true if the selections are locked.
35221      * @return {Boolean}
35222      */
35223     isLocked : function(){
35224         return this.locked;
35225     }
35226 });/*
35227  * Based on:
35228  * Ext JS Library 1.1.1
35229  * Copyright(c) 2006-2007, Ext JS, LLC.
35230  *
35231  * Originally Released Under LGPL - original licence link has changed is not relivant.
35232  *
35233  * Fork - LGPL
35234  * <script type="text/javascript">
35235  */
35236 /**
35237  * @extends Roo.grid.AbstractSelectionModel
35238  * @class Roo.grid.RowSelectionModel
35239  * The default SelectionModel used by {@link Roo.grid.Grid}.
35240  * It supports multiple selections and keyboard selection/navigation. 
35241  * @constructor
35242  * @param {Object} config
35243  */
35244 Roo.grid.RowSelectionModel = function(config){
35245     Roo.apply(this, config);
35246     this.selections = new Roo.util.MixedCollection(false, function(o){
35247         return o.id;
35248     });
35249
35250     this.last = false;
35251     this.lastActive = false;
35252
35253     this.addEvents({
35254         /**
35255              * @event selectionchange
35256              * Fires when the selection changes
35257              * @param {SelectionModel} this
35258              */
35259             "selectionchange" : true,
35260         /**
35261              * @event afterselectionchange
35262              * Fires after the selection changes (eg. by key press or clicking)
35263              * @param {SelectionModel} this
35264              */
35265             "afterselectionchange" : true,
35266         /**
35267              * @event beforerowselect
35268              * Fires when a row is selected being selected, return false to cancel.
35269              * @param {SelectionModel} this
35270              * @param {Number} rowIndex The selected index
35271              * @param {Boolean} keepExisting False if other selections will be cleared
35272              */
35273             "beforerowselect" : true,
35274         /**
35275              * @event rowselect
35276              * Fires when a row is selected.
35277              * @param {SelectionModel} this
35278              * @param {Number} rowIndex The selected index
35279              * @param {Roo.data.Record} r The record
35280              */
35281             "rowselect" : true,
35282         /**
35283              * @event rowdeselect
35284              * Fires when a row is deselected.
35285              * @param {SelectionModel} this
35286              * @param {Number} rowIndex The selected index
35287              */
35288         "rowdeselect" : true
35289     });
35290     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35291     this.locked = false;
35292 };
35293
35294 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35295     /**
35296      * @cfg {Boolean} singleSelect
35297      * True to allow selection of only one row at a time (defaults to false)
35298      */
35299     singleSelect : false,
35300
35301     // private
35302     initEvents : function(){
35303
35304         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35305             this.grid.on("mousedown", this.handleMouseDown, this);
35306         }else{ // allow click to work like normal
35307             this.grid.on("rowclick", this.handleDragableRowClick, this);
35308         }
35309
35310         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35311             "up" : function(e){
35312                 if(!e.shiftKey){
35313                     this.selectPrevious(e.shiftKey);
35314                 }else if(this.last !== false && this.lastActive !== false){
35315                     var last = this.last;
35316                     this.selectRange(this.last,  this.lastActive-1);
35317                     this.grid.getView().focusRow(this.lastActive);
35318                     if(last !== false){
35319                         this.last = last;
35320                     }
35321                 }else{
35322                     this.selectFirstRow();
35323                 }
35324                 this.fireEvent("afterselectionchange", this);
35325             },
35326             "down" : function(e){
35327                 if(!e.shiftKey){
35328                     this.selectNext(e.shiftKey);
35329                 }else if(this.last !== false && this.lastActive !== false){
35330                     var last = this.last;
35331                     this.selectRange(this.last,  this.lastActive+1);
35332                     this.grid.getView().focusRow(this.lastActive);
35333                     if(last !== false){
35334                         this.last = last;
35335                     }
35336                 }else{
35337                     this.selectFirstRow();
35338                 }
35339                 this.fireEvent("afterselectionchange", this);
35340             },
35341             scope: this
35342         });
35343
35344         var view = this.grid.view;
35345         view.on("refresh", this.onRefresh, this);
35346         view.on("rowupdated", this.onRowUpdated, this);
35347         view.on("rowremoved", this.onRemove, this);
35348     },
35349
35350     // private
35351     onRefresh : function(){
35352         var ds = this.grid.dataSource, i, v = this.grid.view;
35353         var s = this.selections;
35354         s.each(function(r){
35355             if((i = ds.indexOfId(r.id)) != -1){
35356                 v.onRowSelect(i);
35357                 s.add(ds.getAt(i)); // updating the selection relate data
35358             }else{
35359                 s.remove(r);
35360             }
35361         });
35362     },
35363
35364     // private
35365     onRemove : function(v, index, r){
35366         this.selections.remove(r);
35367     },
35368
35369     // private
35370     onRowUpdated : function(v, index, r){
35371         if(this.isSelected(r)){
35372             v.onRowSelect(index);
35373         }
35374     },
35375
35376     /**
35377      * Select records.
35378      * @param {Array} records The records to select
35379      * @param {Boolean} keepExisting (optional) True to keep existing selections
35380      */
35381     selectRecords : function(records, keepExisting){
35382         if(!keepExisting){
35383             this.clearSelections();
35384         }
35385         var ds = this.grid.dataSource;
35386         for(var i = 0, len = records.length; i < len; i++){
35387             this.selectRow(ds.indexOf(records[i]), true);
35388         }
35389     },
35390
35391     /**
35392      * Gets the number of selected rows.
35393      * @return {Number}
35394      */
35395     getCount : function(){
35396         return this.selections.length;
35397     },
35398
35399     /**
35400      * Selects the first row in the grid.
35401      */
35402     selectFirstRow : function(){
35403         this.selectRow(0);
35404     },
35405
35406     /**
35407      * Select the last row.
35408      * @param {Boolean} keepExisting (optional) True to keep existing selections
35409      */
35410     selectLastRow : function(keepExisting){
35411         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
35412     },
35413
35414     /**
35415      * Selects the row immediately following the last selected row.
35416      * @param {Boolean} keepExisting (optional) True to keep existing selections
35417      */
35418     selectNext : function(keepExisting){
35419         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
35420             this.selectRow(this.last+1, keepExisting);
35421             this.grid.getView().focusRow(this.last);
35422         }
35423     },
35424
35425     /**
35426      * Selects the row that precedes the last selected row.
35427      * @param {Boolean} keepExisting (optional) True to keep existing selections
35428      */
35429     selectPrevious : function(keepExisting){
35430         if(this.last){
35431             this.selectRow(this.last-1, keepExisting);
35432             this.grid.getView().focusRow(this.last);
35433         }
35434     },
35435
35436     /**
35437      * Returns the selected records
35438      * @return {Array} Array of selected records
35439      */
35440     getSelections : function(){
35441         return [].concat(this.selections.items);
35442     },
35443
35444     /**
35445      * Returns the first selected record.
35446      * @return {Record}
35447      */
35448     getSelected : function(){
35449         return this.selections.itemAt(0);
35450     },
35451
35452
35453     /**
35454      * Clears all selections.
35455      */
35456     clearSelections : function(fast){
35457         if(this.locked) {
35458             return;
35459         }
35460         if(fast !== true){
35461             var ds = this.grid.dataSource;
35462             var s = this.selections;
35463             s.each(function(r){
35464                 this.deselectRow(ds.indexOfId(r.id));
35465             }, this);
35466             s.clear();
35467         }else{
35468             this.selections.clear();
35469         }
35470         this.last = false;
35471     },
35472
35473
35474     /**
35475      * Selects all rows.
35476      */
35477     selectAll : function(){
35478         if(this.locked) {
35479             return;
35480         }
35481         this.selections.clear();
35482         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
35483             this.selectRow(i, true);
35484         }
35485     },
35486
35487     /**
35488      * Returns True if there is a selection.
35489      * @return {Boolean}
35490      */
35491     hasSelection : function(){
35492         return this.selections.length > 0;
35493     },
35494
35495     /**
35496      * Returns True if the specified row is selected.
35497      * @param {Number/Record} record The record or index of the record to check
35498      * @return {Boolean}
35499      */
35500     isSelected : function(index){
35501         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
35502         return (r && this.selections.key(r.id) ? true : false);
35503     },
35504
35505     /**
35506      * Returns True if the specified record id is selected.
35507      * @param {String} id The id of record to check
35508      * @return {Boolean}
35509      */
35510     isIdSelected : function(id){
35511         return (this.selections.key(id) ? true : false);
35512     },
35513
35514     // private
35515     handleMouseDown : function(e, t){
35516         var view = this.grid.getView(), rowIndex;
35517         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
35518             return;
35519         };
35520         if(e.shiftKey && this.last !== false){
35521             var last = this.last;
35522             this.selectRange(last, rowIndex, e.ctrlKey);
35523             this.last = last; // reset the last
35524             view.focusRow(rowIndex);
35525         }else{
35526             var isSelected = this.isSelected(rowIndex);
35527             if(e.button !== 0 && isSelected){
35528                 view.focusRow(rowIndex);
35529             }else if(e.ctrlKey && isSelected){
35530                 this.deselectRow(rowIndex);
35531             }else if(!isSelected){
35532                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
35533                 view.focusRow(rowIndex);
35534             }
35535         }
35536         this.fireEvent("afterselectionchange", this);
35537     },
35538     // private
35539     handleDragableRowClick :  function(grid, rowIndex, e) 
35540     {
35541         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
35542             this.selectRow(rowIndex, false);
35543             grid.view.focusRow(rowIndex);
35544              this.fireEvent("afterselectionchange", this);
35545         }
35546     },
35547     
35548     /**
35549      * Selects multiple rows.
35550      * @param {Array} rows Array of the indexes of the row to select
35551      * @param {Boolean} keepExisting (optional) True to keep existing selections
35552      */
35553     selectRows : function(rows, keepExisting){
35554         if(!keepExisting){
35555             this.clearSelections();
35556         }
35557         for(var i = 0, len = rows.length; i < len; i++){
35558             this.selectRow(rows[i], true);
35559         }
35560     },
35561
35562     /**
35563      * Selects a range of rows. All rows in between startRow and endRow are also selected.
35564      * @param {Number} startRow The index of the first row in the range
35565      * @param {Number} endRow The index of the last row in the range
35566      * @param {Boolean} keepExisting (optional) True to retain existing selections
35567      */
35568     selectRange : function(startRow, endRow, keepExisting){
35569         if(this.locked) {
35570             return;
35571         }
35572         if(!keepExisting){
35573             this.clearSelections();
35574         }
35575         if(startRow <= endRow){
35576             for(var i = startRow; i <= endRow; i++){
35577                 this.selectRow(i, true);
35578             }
35579         }else{
35580             for(var i = startRow; i >= endRow; i--){
35581                 this.selectRow(i, true);
35582             }
35583         }
35584     },
35585
35586     /**
35587      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
35588      * @param {Number} startRow The index of the first row in the range
35589      * @param {Number} endRow The index of the last row in the range
35590      */
35591     deselectRange : function(startRow, endRow, preventViewNotify){
35592         if(this.locked) {
35593             return;
35594         }
35595         for(var i = startRow; i <= endRow; i++){
35596             this.deselectRow(i, preventViewNotify);
35597         }
35598     },
35599
35600     /**
35601      * Selects a row.
35602      * @param {Number} row The index of the row to select
35603      * @param {Boolean} keepExisting (optional) True to keep existing selections
35604      */
35605     selectRow : function(index, keepExisting, preventViewNotify){
35606         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
35607             return;
35608         }
35609         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
35610             if(!keepExisting || this.singleSelect){
35611                 this.clearSelections();
35612             }
35613             var r = this.grid.dataSource.getAt(index);
35614             this.selections.add(r);
35615             this.last = this.lastActive = index;
35616             if(!preventViewNotify){
35617                 this.grid.getView().onRowSelect(index);
35618             }
35619             this.fireEvent("rowselect", this, index, r);
35620             this.fireEvent("selectionchange", this);
35621         }
35622     },
35623
35624     /**
35625      * Deselects a row.
35626      * @param {Number} row The index of the row to deselect
35627      */
35628     deselectRow : function(index, preventViewNotify){
35629         if(this.locked) {
35630             return;
35631         }
35632         if(this.last == index){
35633             this.last = false;
35634         }
35635         if(this.lastActive == index){
35636             this.lastActive = false;
35637         }
35638         var r = this.grid.dataSource.getAt(index);
35639         this.selections.remove(r);
35640         if(!preventViewNotify){
35641             this.grid.getView().onRowDeselect(index);
35642         }
35643         this.fireEvent("rowdeselect", this, index);
35644         this.fireEvent("selectionchange", this);
35645     },
35646
35647     // private
35648     restoreLast : function(){
35649         if(this._last){
35650             this.last = this._last;
35651         }
35652     },
35653
35654     // private
35655     acceptsNav : function(row, col, cm){
35656         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35657     },
35658
35659     // private
35660     onEditorKey : function(field, e){
35661         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35662         if(k == e.TAB){
35663             e.stopEvent();
35664             ed.completeEdit();
35665             if(e.shiftKey){
35666                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35667             }else{
35668                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35669             }
35670         }else if(k == e.ENTER && !e.ctrlKey){
35671             e.stopEvent();
35672             ed.completeEdit();
35673             if(e.shiftKey){
35674                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
35675             }else{
35676                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
35677             }
35678         }else if(k == e.ESC){
35679             ed.cancelEdit();
35680         }
35681         if(newCell){
35682             g.startEditing(newCell[0], newCell[1]);
35683         }
35684     }
35685 });/*
35686  * Based on:
35687  * Ext JS Library 1.1.1
35688  * Copyright(c) 2006-2007, Ext JS, LLC.
35689  *
35690  * Originally Released Under LGPL - original licence link has changed is not relivant.
35691  *
35692  * Fork - LGPL
35693  * <script type="text/javascript">
35694  */
35695 /**
35696  * @class Roo.grid.CellSelectionModel
35697  * @extends Roo.grid.AbstractSelectionModel
35698  * This class provides the basic implementation for cell selection in a grid.
35699  * @constructor
35700  * @param {Object} config The object containing the configuration of this model.
35701  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
35702  */
35703 Roo.grid.CellSelectionModel = function(config){
35704     Roo.apply(this, config);
35705
35706     this.selection = null;
35707
35708     this.addEvents({
35709         /**
35710              * @event beforerowselect
35711              * Fires before a cell is selected.
35712              * @param {SelectionModel} this
35713              * @param {Number} rowIndex The selected row index
35714              * @param {Number} colIndex The selected cell index
35715              */
35716             "beforecellselect" : true,
35717         /**
35718              * @event cellselect
35719              * Fires when a cell is selected.
35720              * @param {SelectionModel} this
35721              * @param {Number} rowIndex The selected row index
35722              * @param {Number} colIndex The selected cell index
35723              */
35724             "cellselect" : true,
35725         /**
35726              * @event selectionchange
35727              * Fires when the active selection changes.
35728              * @param {SelectionModel} this
35729              * @param {Object} selection null for no selection or an object (o) with two properties
35730                 <ul>
35731                 <li>o.record: the record object for the row the selection is in</li>
35732                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
35733                 </ul>
35734              */
35735             "selectionchange" : true,
35736         /**
35737              * @event tabend
35738              * Fires when the tab (or enter) was pressed on the last editable cell
35739              * You can use this to trigger add new row.
35740              * @param {SelectionModel} this
35741              */
35742             "tabend" : true,
35743          /**
35744              * @event beforeeditnext
35745              * Fires before the next editable sell is made active
35746              * You can use this to skip to another cell or fire the tabend
35747              *    if you set cell to false
35748              * @param {Object} eventdata object : { cell : [ row, col ] } 
35749              */
35750             "beforeeditnext" : true
35751     });
35752     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
35753 };
35754
35755 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
35756     
35757     enter_is_tab: false,
35758
35759     /** @ignore */
35760     initEvents : function(){
35761         this.grid.on("mousedown", this.handleMouseDown, this);
35762         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
35763         var view = this.grid.view;
35764         view.on("refresh", this.onViewChange, this);
35765         view.on("rowupdated", this.onRowUpdated, this);
35766         view.on("beforerowremoved", this.clearSelections, this);
35767         view.on("beforerowsinserted", this.clearSelections, this);
35768         if(this.grid.isEditor){
35769             this.grid.on("beforeedit", this.beforeEdit,  this);
35770         }
35771     },
35772
35773         //private
35774     beforeEdit : function(e){
35775         this.select(e.row, e.column, false, true, e.record);
35776     },
35777
35778         //private
35779     onRowUpdated : function(v, index, r){
35780         if(this.selection && this.selection.record == r){
35781             v.onCellSelect(index, this.selection.cell[1]);
35782         }
35783     },
35784
35785         //private
35786     onViewChange : function(){
35787         this.clearSelections(true);
35788     },
35789
35790         /**
35791          * Returns the currently selected cell,.
35792          * @return {Array} The selected cell (row, column) or null if none selected.
35793          */
35794     getSelectedCell : function(){
35795         return this.selection ? this.selection.cell : null;
35796     },
35797
35798     /**
35799      * Clears all selections.
35800      * @param {Boolean} true to prevent the gridview from being notified about the change.
35801      */
35802     clearSelections : function(preventNotify){
35803         var s = this.selection;
35804         if(s){
35805             if(preventNotify !== true){
35806                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
35807             }
35808             this.selection = null;
35809             this.fireEvent("selectionchange", this, null);
35810         }
35811     },
35812
35813     /**
35814      * Returns true if there is a selection.
35815      * @return {Boolean}
35816      */
35817     hasSelection : function(){
35818         return this.selection ? true : false;
35819     },
35820
35821     /** @ignore */
35822     handleMouseDown : function(e, t){
35823         var v = this.grid.getView();
35824         if(this.isLocked()){
35825             return;
35826         };
35827         var row = v.findRowIndex(t);
35828         var cell = v.findCellIndex(t);
35829         if(row !== false && cell !== false){
35830             this.select(row, cell);
35831         }
35832     },
35833
35834     /**
35835      * Selects a cell.
35836      * @param {Number} rowIndex
35837      * @param {Number} collIndex
35838      */
35839     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
35840         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
35841             this.clearSelections();
35842             r = r || this.grid.dataSource.getAt(rowIndex);
35843             this.selection = {
35844                 record : r,
35845                 cell : [rowIndex, colIndex]
35846             };
35847             if(!preventViewNotify){
35848                 var v = this.grid.getView();
35849                 v.onCellSelect(rowIndex, colIndex);
35850                 if(preventFocus !== true){
35851                     v.focusCell(rowIndex, colIndex);
35852                 }
35853             }
35854             this.fireEvent("cellselect", this, rowIndex, colIndex);
35855             this.fireEvent("selectionchange", this, this.selection);
35856         }
35857     },
35858
35859         //private
35860     isSelectable : function(rowIndex, colIndex, cm){
35861         return !cm.isHidden(colIndex);
35862     },
35863
35864     /** @ignore */
35865     handleKeyDown : function(e){
35866         //Roo.log('Cell Sel Model handleKeyDown');
35867         if(!e.isNavKeyPress()){
35868             return;
35869         }
35870         var g = this.grid, s = this.selection;
35871         if(!s){
35872             e.stopEvent();
35873             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
35874             if(cell){
35875                 this.select(cell[0], cell[1]);
35876             }
35877             return;
35878         }
35879         var sm = this;
35880         var walk = function(row, col, step){
35881             return g.walkCells(row, col, step, sm.isSelectable,  sm);
35882         };
35883         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
35884         var newCell;
35885
35886       
35887
35888         switch(k){
35889             case e.TAB:
35890                 // handled by onEditorKey
35891                 if (g.isEditor && g.editing) {
35892                     return;
35893                 }
35894                 if(e.shiftKey) {
35895                     newCell = walk(r, c-1, -1);
35896                 } else {
35897                     newCell = walk(r, c+1, 1);
35898                 }
35899                 break;
35900             
35901             case e.DOWN:
35902                newCell = walk(r+1, c, 1);
35903                 break;
35904             
35905             case e.UP:
35906                 newCell = walk(r-1, c, -1);
35907                 break;
35908             
35909             case e.RIGHT:
35910                 newCell = walk(r, c+1, 1);
35911                 break;
35912             
35913             case e.LEFT:
35914                 newCell = walk(r, c-1, -1);
35915                 break;
35916             
35917             case e.ENTER:
35918                 
35919                 if(g.isEditor && !g.editing){
35920                    g.startEditing(r, c);
35921                    e.stopEvent();
35922                    return;
35923                 }
35924                 
35925                 
35926              break;
35927         };
35928         if(newCell){
35929             this.select(newCell[0], newCell[1]);
35930             e.stopEvent();
35931             
35932         }
35933     },
35934
35935     acceptsNav : function(row, col, cm){
35936         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35937     },
35938     /**
35939      * Selects a cell.
35940      * @param {Number} field (not used) - as it's normally used as a listener
35941      * @param {Number} e - event - fake it by using
35942      *
35943      * var e = Roo.EventObjectImpl.prototype;
35944      * e.keyCode = e.TAB
35945      *
35946      * 
35947      */
35948     onEditorKey : function(field, e){
35949         
35950         var k = e.getKey(),
35951             newCell,
35952             g = this.grid,
35953             ed = g.activeEditor,
35954             forward = false;
35955         ///Roo.log('onEditorKey' + k);
35956         
35957         
35958         if (this.enter_is_tab && k == e.ENTER) {
35959             k = e.TAB;
35960         }
35961         
35962         if(k == e.TAB){
35963             if(e.shiftKey){
35964                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35965             }else{
35966                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35967                 forward = true;
35968             }
35969             
35970             e.stopEvent();
35971             
35972         } else if(k == e.ENTER &&  !e.ctrlKey){
35973             ed.completeEdit();
35974             e.stopEvent();
35975             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35976         
35977                 } else if(k == e.ESC){
35978             ed.cancelEdit();
35979         }
35980                 
35981         if (newCell) {
35982             var ecall = { cell : newCell, forward : forward };
35983             this.fireEvent('beforeeditnext', ecall );
35984             newCell = ecall.cell;
35985                         forward = ecall.forward;
35986         }
35987                 
35988         if(newCell){
35989             //Roo.log('next cell after edit');
35990             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
35991         } else if (forward) {
35992             // tabbed past last
35993             this.fireEvent.defer(100, this, ['tabend',this]);
35994         }
35995     }
35996 });/*
35997  * Based on:
35998  * Ext JS Library 1.1.1
35999  * Copyright(c) 2006-2007, Ext JS, LLC.
36000  *
36001  * Originally Released Under LGPL - original licence link has changed is not relivant.
36002  *
36003  * Fork - LGPL
36004  * <script type="text/javascript">
36005  */
36006  
36007 /**
36008  * @class Roo.grid.EditorGrid
36009  * @extends Roo.grid.Grid
36010  * Class for creating and editable grid.
36011  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36012  * The container MUST have some type of size defined for the grid to fill. The container will be 
36013  * automatically set to position relative if it isn't already.
36014  * @param {Object} dataSource The data model to bind to
36015  * @param {Object} colModel The column model with info about this grid's columns
36016  */
36017 Roo.grid.EditorGrid = function(container, config){
36018     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36019     this.getGridEl().addClass("xedit-grid");
36020
36021     if(!this.selModel){
36022         this.selModel = new Roo.grid.CellSelectionModel();
36023     }
36024
36025     this.activeEditor = null;
36026
36027         this.addEvents({
36028             /**
36029              * @event beforeedit
36030              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36031              * <ul style="padding:5px;padding-left:16px;">
36032              * <li>grid - This grid</li>
36033              * <li>record - The record being edited</li>
36034              * <li>field - The field name being edited</li>
36035              * <li>value - The value for the field being edited.</li>
36036              * <li>row - The grid row index</li>
36037              * <li>column - The grid column index</li>
36038              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36039              * </ul>
36040              * @param {Object} e An edit event (see above for description)
36041              */
36042             "beforeedit" : true,
36043             /**
36044              * @event afteredit
36045              * Fires after a cell is edited. <br />
36046              * <ul style="padding:5px;padding-left:16px;">
36047              * <li>grid - This grid</li>
36048              * <li>record - The record being edited</li>
36049              * <li>field - The field name being edited</li>
36050              * <li>value - The value being set</li>
36051              * <li>originalValue - The original value for the field, before the edit.</li>
36052              * <li>row - The grid row index</li>
36053              * <li>column - The grid column index</li>
36054              * </ul>
36055              * @param {Object} e An edit event (see above for description)
36056              */
36057             "afteredit" : true,
36058             /**
36059              * @event validateedit
36060              * Fires after a cell is edited, but before the value is set in the record. 
36061          * You can use this to modify the value being set in the field, Return false
36062              * to cancel the change. The edit event object has the following properties <br />
36063              * <ul style="padding:5px;padding-left:16px;">
36064          * <li>editor - This editor</li>
36065              * <li>grid - This grid</li>
36066              * <li>record - The record being edited</li>
36067              * <li>field - The field name being edited</li>
36068              * <li>value - The value being set</li>
36069              * <li>originalValue - The original value for the field, before the edit.</li>
36070              * <li>row - The grid row index</li>
36071              * <li>column - The grid column index</li>
36072              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36073              * </ul>
36074              * @param {Object} e An edit event (see above for description)
36075              */
36076             "validateedit" : true
36077         });
36078     this.on("bodyscroll", this.stopEditing,  this);
36079     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36080 };
36081
36082 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36083     /**
36084      * @cfg {Number} clicksToEdit
36085      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36086      */
36087     clicksToEdit: 2,
36088
36089     // private
36090     isEditor : true,
36091     // private
36092     trackMouseOver: false, // causes very odd FF errors
36093
36094     onCellDblClick : function(g, row, col){
36095         this.startEditing(row, col);
36096     },
36097
36098     onEditComplete : function(ed, value, startValue){
36099         this.editing = false;
36100         this.activeEditor = null;
36101         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36102         var r = ed.record;
36103         var field = this.colModel.getDataIndex(ed.col);
36104         var e = {
36105             grid: this,
36106             record: r,
36107             field: field,
36108             originalValue: startValue,
36109             value: value,
36110             row: ed.row,
36111             column: ed.col,
36112             cancel:false,
36113             editor: ed
36114         };
36115         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36116         cell.show();
36117           
36118         if(String(value) !== String(startValue)){
36119             
36120             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36121                 r.set(field, e.value);
36122                 // if we are dealing with a combo box..
36123                 // then we also set the 'name' colum to be the displayField
36124                 if (ed.field.displayField && ed.field.name) {
36125                     r.set(ed.field.name, ed.field.el.dom.value);
36126                 }
36127                 
36128                 delete e.cancel; //?? why!!!
36129                 this.fireEvent("afteredit", e);
36130             }
36131         } else {
36132             this.fireEvent("afteredit", e); // always fire it!
36133         }
36134         this.view.focusCell(ed.row, ed.col);
36135     },
36136
36137     /**
36138      * Starts editing the specified for the specified row/column
36139      * @param {Number} rowIndex
36140      * @param {Number} colIndex
36141      */
36142     startEditing : function(row, col){
36143         this.stopEditing();
36144         if(this.colModel.isCellEditable(col, row)){
36145             this.view.ensureVisible(row, col, true);
36146           
36147             var r = this.dataSource.getAt(row);
36148             var field = this.colModel.getDataIndex(col);
36149             var cell = Roo.get(this.view.getCell(row,col));
36150             var e = {
36151                 grid: this,
36152                 record: r,
36153                 field: field,
36154                 value: r.data[field],
36155                 row: row,
36156                 column: col,
36157                 cancel:false 
36158             };
36159             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36160                 this.editing = true;
36161                 var ed = this.colModel.getCellEditor(col, row);
36162                 
36163                 if (!ed) {
36164                     return;
36165                 }
36166                 if(!ed.rendered){
36167                     ed.render(ed.parentEl || document.body);
36168                 }
36169                 ed.field.reset();
36170                
36171                 cell.hide();
36172                 
36173                 (function(){ // complex but required for focus issues in safari, ie and opera
36174                     ed.row = row;
36175                     ed.col = col;
36176                     ed.record = r;
36177                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
36178                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
36179                     this.activeEditor = ed;
36180                     var v = r.data[field];
36181                     ed.startEdit(this.view.getCell(row, col), v);
36182                     // combo's with 'displayField and name set
36183                     if (ed.field.displayField && ed.field.name) {
36184                         ed.field.el.dom.value = r.data[ed.field.name];
36185                     }
36186                     
36187                     
36188                 }).defer(50, this);
36189             }
36190         }
36191     },
36192         
36193     /**
36194      * Stops any active editing
36195      */
36196     stopEditing : function(){
36197         if(this.activeEditor){
36198             this.activeEditor.completeEdit();
36199         }
36200         this.activeEditor = null;
36201     },
36202         
36203          /**
36204      * Called to get grid's drag proxy text, by default returns this.ddText.
36205      * @return {String}
36206      */
36207     getDragDropText : function(){
36208         var count = this.selModel.getSelectedCell() ? 1 : 0;
36209         return String.format(this.ddText, count, count == 1 ? '' : 's');
36210     }
36211         
36212 });/*
36213  * Based on:
36214  * Ext JS Library 1.1.1
36215  * Copyright(c) 2006-2007, Ext JS, LLC.
36216  *
36217  * Originally Released Under LGPL - original licence link has changed is not relivant.
36218  *
36219  * Fork - LGPL
36220  * <script type="text/javascript">
36221  */
36222
36223 // private - not really -- you end up using it !
36224 // This is a support class used internally by the Grid components
36225
36226 /**
36227  * @class Roo.grid.GridEditor
36228  * @extends Roo.Editor
36229  * Class for creating and editable grid elements.
36230  * @param {Object} config any settings (must include field)
36231  */
36232 Roo.grid.GridEditor = function(field, config){
36233     if (!config && field.field) {
36234         config = field;
36235         field = Roo.factory(config.field, Roo.form);
36236     }
36237     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36238     field.monitorTab = false;
36239 };
36240
36241 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36242     
36243     /**
36244      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36245      */
36246     
36247     alignment: "tl-tl",
36248     autoSize: "width",
36249     hideEl : false,
36250     cls: "x-small-editor x-grid-editor",
36251     shim:false,
36252     shadow:"frame"
36253 });/*
36254  * Based on:
36255  * Ext JS Library 1.1.1
36256  * Copyright(c) 2006-2007, Ext JS, LLC.
36257  *
36258  * Originally Released Under LGPL - original licence link has changed is not relivant.
36259  *
36260  * Fork - LGPL
36261  * <script type="text/javascript">
36262  */
36263   
36264
36265   
36266 Roo.grid.PropertyRecord = Roo.data.Record.create([
36267     {name:'name',type:'string'},  'value'
36268 ]);
36269
36270
36271 Roo.grid.PropertyStore = function(grid, source){
36272     this.grid = grid;
36273     this.store = new Roo.data.Store({
36274         recordType : Roo.grid.PropertyRecord
36275     });
36276     this.store.on('update', this.onUpdate,  this);
36277     if(source){
36278         this.setSource(source);
36279     }
36280     Roo.grid.PropertyStore.superclass.constructor.call(this);
36281 };
36282
36283
36284
36285 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36286     setSource : function(o){
36287         this.source = o;
36288         this.store.removeAll();
36289         var data = [];
36290         for(var k in o){
36291             if(this.isEditableValue(o[k])){
36292                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36293             }
36294         }
36295         this.store.loadRecords({records: data}, {}, true);
36296     },
36297
36298     onUpdate : function(ds, record, type){
36299         if(type == Roo.data.Record.EDIT){
36300             var v = record.data['value'];
36301             var oldValue = record.modified['value'];
36302             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36303                 this.source[record.id] = v;
36304                 record.commit();
36305                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36306             }else{
36307                 record.reject();
36308             }
36309         }
36310     },
36311
36312     getProperty : function(row){
36313        return this.store.getAt(row);
36314     },
36315
36316     isEditableValue: function(val){
36317         if(val && val instanceof Date){
36318             return true;
36319         }else if(typeof val == 'object' || typeof val == 'function'){
36320             return false;
36321         }
36322         return true;
36323     },
36324
36325     setValue : function(prop, value){
36326         this.source[prop] = value;
36327         this.store.getById(prop).set('value', value);
36328     },
36329
36330     getSource : function(){
36331         return this.source;
36332     }
36333 });
36334
36335 Roo.grid.PropertyColumnModel = function(grid, store){
36336     this.grid = grid;
36337     var g = Roo.grid;
36338     g.PropertyColumnModel.superclass.constructor.call(this, [
36339         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36340         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36341     ]);
36342     this.store = store;
36343     this.bselect = Roo.DomHelper.append(document.body, {
36344         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36345             {tag: 'option', value: 'true', html: 'true'},
36346             {tag: 'option', value: 'false', html: 'false'}
36347         ]
36348     });
36349     Roo.id(this.bselect);
36350     var f = Roo.form;
36351     this.editors = {
36352         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36353         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36354         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36355         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36356         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36357     };
36358     this.renderCellDelegate = this.renderCell.createDelegate(this);
36359     this.renderPropDelegate = this.renderProp.createDelegate(this);
36360 };
36361
36362 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36363     
36364     
36365     nameText : 'Name',
36366     valueText : 'Value',
36367     
36368     dateFormat : 'm/j/Y',
36369     
36370     
36371     renderDate : function(dateVal){
36372         return dateVal.dateFormat(this.dateFormat);
36373     },
36374
36375     renderBool : function(bVal){
36376         return bVal ? 'true' : 'false';
36377     },
36378
36379     isCellEditable : function(colIndex, rowIndex){
36380         return colIndex == 1;
36381     },
36382
36383     getRenderer : function(col){
36384         return col == 1 ?
36385             this.renderCellDelegate : this.renderPropDelegate;
36386     },
36387
36388     renderProp : function(v){
36389         return this.getPropertyName(v);
36390     },
36391
36392     renderCell : function(val){
36393         var rv = val;
36394         if(val instanceof Date){
36395             rv = this.renderDate(val);
36396         }else if(typeof val == 'boolean'){
36397             rv = this.renderBool(val);
36398         }
36399         return Roo.util.Format.htmlEncode(rv);
36400     },
36401
36402     getPropertyName : function(name){
36403         var pn = this.grid.propertyNames;
36404         return pn && pn[name] ? pn[name] : name;
36405     },
36406
36407     getCellEditor : function(colIndex, rowIndex){
36408         var p = this.store.getProperty(rowIndex);
36409         var n = p.data['name'], val = p.data['value'];
36410         
36411         if(typeof(this.grid.customEditors[n]) == 'string'){
36412             return this.editors[this.grid.customEditors[n]];
36413         }
36414         if(typeof(this.grid.customEditors[n]) != 'undefined'){
36415             return this.grid.customEditors[n];
36416         }
36417         if(val instanceof Date){
36418             return this.editors['date'];
36419         }else if(typeof val == 'number'){
36420             return this.editors['number'];
36421         }else if(typeof val == 'boolean'){
36422             return this.editors['boolean'];
36423         }else{
36424             return this.editors['string'];
36425         }
36426     }
36427 });
36428
36429 /**
36430  * @class Roo.grid.PropertyGrid
36431  * @extends Roo.grid.EditorGrid
36432  * This class represents the  interface of a component based property grid control.
36433  * <br><br>Usage:<pre><code>
36434  var grid = new Roo.grid.PropertyGrid("my-container-id", {
36435       
36436  });
36437  // set any options
36438  grid.render();
36439  * </code></pre>
36440   
36441  * @constructor
36442  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36443  * The container MUST have some type of size defined for the grid to fill. The container will be
36444  * automatically set to position relative if it isn't already.
36445  * @param {Object} config A config object that sets properties on this grid.
36446  */
36447 Roo.grid.PropertyGrid = function(container, config){
36448     config = config || {};
36449     var store = new Roo.grid.PropertyStore(this);
36450     this.store = store;
36451     var cm = new Roo.grid.PropertyColumnModel(this, store);
36452     store.store.sort('name', 'ASC');
36453     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
36454         ds: store.store,
36455         cm: cm,
36456         enableColLock:false,
36457         enableColumnMove:false,
36458         stripeRows:false,
36459         trackMouseOver: false,
36460         clicksToEdit:1
36461     }, config));
36462     this.getGridEl().addClass('x-props-grid');
36463     this.lastEditRow = null;
36464     this.on('columnresize', this.onColumnResize, this);
36465     this.addEvents({
36466          /**
36467              * @event beforepropertychange
36468              * Fires before a property changes (return false to stop?)
36469              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36470              * @param {String} id Record Id
36471              * @param {String} newval New Value
36472          * @param {String} oldval Old Value
36473              */
36474         "beforepropertychange": true,
36475         /**
36476              * @event propertychange
36477              * Fires after a property changes
36478              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36479              * @param {String} id Record Id
36480              * @param {String} newval New Value
36481          * @param {String} oldval Old Value
36482              */
36483         "propertychange": true
36484     });
36485     this.customEditors = this.customEditors || {};
36486 };
36487 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
36488     
36489      /**
36490      * @cfg {Object} customEditors map of colnames=> custom editors.
36491      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
36492      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
36493      * false disables editing of the field.
36494          */
36495     
36496       /**
36497      * @cfg {Object} propertyNames map of property Names to their displayed value
36498          */
36499     
36500     render : function(){
36501         Roo.grid.PropertyGrid.superclass.render.call(this);
36502         this.autoSize.defer(100, this);
36503     },
36504
36505     autoSize : function(){
36506         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
36507         if(this.view){
36508             this.view.fitColumns();
36509         }
36510     },
36511
36512     onColumnResize : function(){
36513         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
36514         this.autoSize();
36515     },
36516     /**
36517      * Sets the data for the Grid
36518      * accepts a Key => Value object of all the elements avaiable.
36519      * @param {Object} data  to appear in grid.
36520      */
36521     setSource : function(source){
36522         this.store.setSource(source);
36523         //this.autoSize();
36524     },
36525     /**
36526      * Gets all the data from the grid.
36527      * @return {Object} data  data stored in grid
36528      */
36529     getSource : function(){
36530         return this.store.getSource();
36531     }
36532 });/*
36533   
36534  * Licence LGPL
36535  
36536  */
36537  
36538 /**
36539  * @class Roo.grid.Calendar
36540  * @extends Roo.util.Grid
36541  * This class extends the Grid to provide a calendar widget
36542  * <br><br>Usage:<pre><code>
36543  var grid = new Roo.grid.Calendar("my-container-id", {
36544      ds: myDataStore,
36545      cm: myColModel,
36546      selModel: mySelectionModel,
36547      autoSizeColumns: true,
36548      monitorWindowResize: false,
36549      trackMouseOver: true
36550      eventstore : real data store..
36551  });
36552  // set any options
36553  grid.render();
36554   
36555   * @constructor
36556  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36557  * The container MUST have some type of size defined for the grid to fill. The container will be
36558  * automatically set to position relative if it isn't already.
36559  * @param {Object} config A config object that sets properties on this grid.
36560  */
36561 Roo.grid.Calendar = function(container, config){
36562         // initialize the container
36563         this.container = Roo.get(container);
36564         this.container.update("");
36565         this.container.setStyle("overflow", "hidden");
36566     this.container.addClass('x-grid-container');
36567
36568     this.id = this.container.id;
36569
36570     Roo.apply(this, config);
36571     // check and correct shorthanded configs
36572     
36573     var rows = [];
36574     var d =1;
36575     for (var r = 0;r < 6;r++) {
36576         
36577         rows[r]=[];
36578         for (var c =0;c < 7;c++) {
36579             rows[r][c]= '';
36580         }
36581     }
36582     if (this.eventStore) {
36583         this.eventStore= Roo.factory(this.eventStore, Roo.data);
36584         this.eventStore.on('load',this.onLoad, this);
36585         this.eventStore.on('beforeload',this.clearEvents, this);
36586          
36587     }
36588     
36589     this.dataSource = new Roo.data.Store({
36590             proxy: new Roo.data.MemoryProxy(rows),
36591             reader: new Roo.data.ArrayReader({}, [
36592                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
36593     });
36594
36595     this.dataSource.load();
36596     this.ds = this.dataSource;
36597     this.ds.xmodule = this.xmodule || false;
36598     
36599     
36600     var cellRender = function(v,x,r)
36601     {
36602         return String.format(
36603             '<div class="fc-day  fc-widget-content"><div>' +
36604                 '<div class="fc-event-container"></div>' +
36605                 '<div class="fc-day-number">{0}</div>'+
36606                 
36607                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
36608             '</div></div>', v);
36609     
36610     }
36611     
36612     
36613     this.colModel = new Roo.grid.ColumnModel( [
36614         {
36615             xtype: 'ColumnModel',
36616             xns: Roo.grid,
36617             dataIndex : 'weekday0',
36618             header : 'Sunday',
36619             renderer : cellRender
36620         },
36621         {
36622             xtype: 'ColumnModel',
36623             xns: Roo.grid,
36624             dataIndex : 'weekday1',
36625             header : 'Monday',
36626             renderer : cellRender
36627         },
36628         {
36629             xtype: 'ColumnModel',
36630             xns: Roo.grid,
36631             dataIndex : 'weekday2',
36632             header : 'Tuesday',
36633             renderer : cellRender
36634         },
36635         {
36636             xtype: 'ColumnModel',
36637             xns: Roo.grid,
36638             dataIndex : 'weekday3',
36639             header : 'Wednesday',
36640             renderer : cellRender
36641         },
36642         {
36643             xtype: 'ColumnModel',
36644             xns: Roo.grid,
36645             dataIndex : 'weekday4',
36646             header : 'Thursday',
36647             renderer : cellRender
36648         },
36649         {
36650             xtype: 'ColumnModel',
36651             xns: Roo.grid,
36652             dataIndex : 'weekday5',
36653             header : 'Friday',
36654             renderer : cellRender
36655         },
36656         {
36657             xtype: 'ColumnModel',
36658             xns: Roo.grid,
36659             dataIndex : 'weekday6',
36660             header : 'Saturday',
36661             renderer : cellRender
36662         }
36663     ]);
36664     this.cm = this.colModel;
36665     this.cm.xmodule = this.xmodule || false;
36666  
36667         
36668           
36669     //this.selModel = new Roo.grid.CellSelectionModel();
36670     //this.sm = this.selModel;
36671     //this.selModel.init(this);
36672     
36673     
36674     if(this.width){
36675         this.container.setWidth(this.width);
36676     }
36677
36678     if(this.height){
36679         this.container.setHeight(this.height);
36680     }
36681     /** @private */
36682         this.addEvents({
36683         // raw events
36684         /**
36685          * @event click
36686          * The raw click event for the entire grid.
36687          * @param {Roo.EventObject} e
36688          */
36689         "click" : true,
36690         /**
36691          * @event dblclick
36692          * The raw dblclick event for the entire grid.
36693          * @param {Roo.EventObject} e
36694          */
36695         "dblclick" : true,
36696         /**
36697          * @event contextmenu
36698          * The raw contextmenu event for the entire grid.
36699          * @param {Roo.EventObject} e
36700          */
36701         "contextmenu" : true,
36702         /**
36703          * @event mousedown
36704          * The raw mousedown event for the entire grid.
36705          * @param {Roo.EventObject} e
36706          */
36707         "mousedown" : true,
36708         /**
36709          * @event mouseup
36710          * The raw mouseup event for the entire grid.
36711          * @param {Roo.EventObject} e
36712          */
36713         "mouseup" : true,
36714         /**
36715          * @event mouseover
36716          * The raw mouseover event for the entire grid.
36717          * @param {Roo.EventObject} e
36718          */
36719         "mouseover" : true,
36720         /**
36721          * @event mouseout
36722          * The raw mouseout event for the entire grid.
36723          * @param {Roo.EventObject} e
36724          */
36725         "mouseout" : true,
36726         /**
36727          * @event keypress
36728          * The raw keypress event for the entire grid.
36729          * @param {Roo.EventObject} e
36730          */
36731         "keypress" : true,
36732         /**
36733          * @event keydown
36734          * The raw keydown event for the entire grid.
36735          * @param {Roo.EventObject} e
36736          */
36737         "keydown" : true,
36738
36739         // custom events
36740
36741         /**
36742          * @event cellclick
36743          * Fires when a cell is clicked
36744          * @param {Grid} this
36745          * @param {Number} rowIndex
36746          * @param {Number} columnIndex
36747          * @param {Roo.EventObject} e
36748          */
36749         "cellclick" : true,
36750         /**
36751          * @event celldblclick
36752          * Fires when a cell is double clicked
36753          * @param {Grid} this
36754          * @param {Number} rowIndex
36755          * @param {Number} columnIndex
36756          * @param {Roo.EventObject} e
36757          */
36758         "celldblclick" : true,
36759         /**
36760          * @event rowclick
36761          * Fires when a row is clicked
36762          * @param {Grid} this
36763          * @param {Number} rowIndex
36764          * @param {Roo.EventObject} e
36765          */
36766         "rowclick" : true,
36767         /**
36768          * @event rowdblclick
36769          * Fires when a row is double clicked
36770          * @param {Grid} this
36771          * @param {Number} rowIndex
36772          * @param {Roo.EventObject} e
36773          */
36774         "rowdblclick" : true,
36775         /**
36776          * @event headerclick
36777          * Fires when a header is clicked
36778          * @param {Grid} this
36779          * @param {Number} columnIndex
36780          * @param {Roo.EventObject} e
36781          */
36782         "headerclick" : true,
36783         /**
36784          * @event headerdblclick
36785          * Fires when a header cell is double clicked
36786          * @param {Grid} this
36787          * @param {Number} columnIndex
36788          * @param {Roo.EventObject} e
36789          */
36790         "headerdblclick" : true,
36791         /**
36792          * @event rowcontextmenu
36793          * Fires when a row is right clicked
36794          * @param {Grid} this
36795          * @param {Number} rowIndex
36796          * @param {Roo.EventObject} e
36797          */
36798         "rowcontextmenu" : true,
36799         /**
36800          * @event cellcontextmenu
36801          * Fires when a cell is right clicked
36802          * @param {Grid} this
36803          * @param {Number} rowIndex
36804          * @param {Number} cellIndex
36805          * @param {Roo.EventObject} e
36806          */
36807          "cellcontextmenu" : true,
36808         /**
36809          * @event headercontextmenu
36810          * Fires when a header is right clicked
36811          * @param {Grid} this
36812          * @param {Number} columnIndex
36813          * @param {Roo.EventObject} e
36814          */
36815         "headercontextmenu" : true,
36816         /**
36817          * @event bodyscroll
36818          * Fires when the body element is scrolled
36819          * @param {Number} scrollLeft
36820          * @param {Number} scrollTop
36821          */
36822         "bodyscroll" : true,
36823         /**
36824          * @event columnresize
36825          * Fires when the user resizes a column
36826          * @param {Number} columnIndex
36827          * @param {Number} newSize
36828          */
36829         "columnresize" : true,
36830         /**
36831          * @event columnmove
36832          * Fires when the user moves a column
36833          * @param {Number} oldIndex
36834          * @param {Number} newIndex
36835          */
36836         "columnmove" : true,
36837         /**
36838          * @event startdrag
36839          * Fires when row(s) start being dragged
36840          * @param {Grid} this
36841          * @param {Roo.GridDD} dd The drag drop object
36842          * @param {event} e The raw browser event
36843          */
36844         "startdrag" : true,
36845         /**
36846          * @event enddrag
36847          * Fires when a drag operation is complete
36848          * @param {Grid} this
36849          * @param {Roo.GridDD} dd The drag drop object
36850          * @param {event} e The raw browser event
36851          */
36852         "enddrag" : true,
36853         /**
36854          * @event dragdrop
36855          * Fires when dragged row(s) are dropped on a valid DD target
36856          * @param {Grid} this
36857          * @param {Roo.GridDD} dd The drag drop object
36858          * @param {String} targetId The target drag drop object
36859          * @param {event} e The raw browser event
36860          */
36861         "dragdrop" : true,
36862         /**
36863          * @event dragover
36864          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
36865          * @param {Grid} this
36866          * @param {Roo.GridDD} dd The drag drop object
36867          * @param {String} targetId The target drag drop object
36868          * @param {event} e The raw browser event
36869          */
36870         "dragover" : true,
36871         /**
36872          * @event dragenter
36873          *  Fires when the dragged row(s) first cross another DD target while being dragged
36874          * @param {Grid} this
36875          * @param {Roo.GridDD} dd The drag drop object
36876          * @param {String} targetId The target drag drop object
36877          * @param {event} e The raw browser event
36878          */
36879         "dragenter" : true,
36880         /**
36881          * @event dragout
36882          * Fires when the dragged row(s) leave another DD target while being dragged
36883          * @param {Grid} this
36884          * @param {Roo.GridDD} dd The drag drop object
36885          * @param {String} targetId The target drag drop object
36886          * @param {event} e The raw browser event
36887          */
36888         "dragout" : true,
36889         /**
36890          * @event rowclass
36891          * Fires when a row is rendered, so you can change add a style to it.
36892          * @param {GridView} gridview   The grid view
36893          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
36894          */
36895         'rowclass' : true,
36896
36897         /**
36898          * @event render
36899          * Fires when the grid is rendered
36900          * @param {Grid} grid
36901          */
36902         'render' : true,
36903             /**
36904              * @event select
36905              * Fires when a date is selected
36906              * @param {DatePicker} this
36907              * @param {Date} date The selected date
36908              */
36909         'select': true,
36910         /**
36911              * @event monthchange
36912              * Fires when the displayed month changes 
36913              * @param {DatePicker} this
36914              * @param {Date} date The selected month
36915              */
36916         'monthchange': true,
36917         /**
36918              * @event evententer
36919              * Fires when mouse over an event
36920              * @param {Calendar} this
36921              * @param {event} Event
36922              */
36923         'evententer': true,
36924         /**
36925              * @event eventleave
36926              * Fires when the mouse leaves an
36927              * @param {Calendar} this
36928              * @param {event}
36929              */
36930         'eventleave': true,
36931         /**
36932              * @event eventclick
36933              * Fires when the mouse click an
36934              * @param {Calendar} this
36935              * @param {event}
36936              */
36937         'eventclick': true,
36938         /**
36939              * @event eventrender
36940              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
36941              * @param {Calendar} this
36942              * @param {data} data to be modified
36943              */
36944         'eventrender': true
36945         
36946     });
36947
36948     Roo.grid.Grid.superclass.constructor.call(this);
36949     this.on('render', function() {
36950         this.view.el.addClass('x-grid-cal'); 
36951         
36952         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
36953
36954     },this);
36955     
36956     if (!Roo.grid.Calendar.style) {
36957         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
36958             
36959             
36960             '.x-grid-cal .x-grid-col' :  {
36961                 height: 'auto !important',
36962                 'vertical-align': 'top'
36963             },
36964             '.x-grid-cal  .fc-event-hori' : {
36965                 height: '14px'
36966             }
36967              
36968             
36969         }, Roo.id());
36970     }
36971
36972     
36973     
36974 };
36975 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
36976     /**
36977      * @cfg {Store} eventStore The store that loads events.
36978      */
36979     eventStore : 25,
36980
36981      
36982     activeDate : false,
36983     startDay : 0,
36984     autoWidth : true,
36985     monitorWindowResize : false,
36986
36987     
36988     resizeColumns : function() {
36989         var col = (this.view.el.getWidth() / 7) - 3;
36990         // loop through cols, and setWidth
36991         for(var i =0 ; i < 7 ; i++){
36992             this.cm.setColumnWidth(i, col);
36993         }
36994     },
36995      setDate :function(date) {
36996         
36997         Roo.log('setDate?');
36998         
36999         this.resizeColumns();
37000         var vd = this.activeDate;
37001         this.activeDate = date;
37002 //        if(vd && this.el){
37003 //            var t = date.getTime();
37004 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37005 //                Roo.log('using add remove');
37006 //                
37007 //                this.fireEvent('monthchange', this, date);
37008 //                
37009 //                this.cells.removeClass("fc-state-highlight");
37010 //                this.cells.each(function(c){
37011 //                   if(c.dateValue == t){
37012 //                       c.addClass("fc-state-highlight");
37013 //                       setTimeout(function(){
37014 //                            try{c.dom.firstChild.focus();}catch(e){}
37015 //                       }, 50);
37016 //                       return false;
37017 //                   }
37018 //                   return true;
37019 //                });
37020 //                return;
37021 //            }
37022 //        }
37023         
37024         var days = date.getDaysInMonth();
37025         
37026         var firstOfMonth = date.getFirstDateOfMonth();
37027         var startingPos = firstOfMonth.getDay()-this.startDay;
37028         
37029         if(startingPos < this.startDay){
37030             startingPos += 7;
37031         }
37032         
37033         var pm = date.add(Date.MONTH, -1);
37034         var prevStart = pm.getDaysInMonth()-startingPos;
37035 //        
37036         
37037         
37038         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37039         
37040         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37041         //this.cells.addClassOnOver('fc-state-hover');
37042         
37043         var cells = this.cells.elements;
37044         var textEls = this.textNodes;
37045         
37046         //Roo.each(cells, function(cell){
37047         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37048         //});
37049         
37050         days += startingPos;
37051
37052         // convert everything to numbers so it's fast
37053         var day = 86400000;
37054         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37055         //Roo.log(d);
37056         //Roo.log(pm);
37057         //Roo.log(prevStart);
37058         
37059         var today = new Date().clearTime().getTime();
37060         var sel = date.clearTime().getTime();
37061         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37062         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37063         var ddMatch = this.disabledDatesRE;
37064         var ddText = this.disabledDatesText;
37065         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37066         var ddaysText = this.disabledDaysText;
37067         var format = this.format;
37068         
37069         var setCellClass = function(cal, cell){
37070             
37071             //Roo.log('set Cell Class');
37072             cell.title = "";
37073             var t = d.getTime();
37074             
37075             //Roo.log(d);
37076             
37077             
37078             cell.dateValue = t;
37079             if(t == today){
37080                 cell.className += " fc-today";
37081                 cell.className += " fc-state-highlight";
37082                 cell.title = cal.todayText;
37083             }
37084             if(t == sel){
37085                 // disable highlight in other month..
37086                 cell.className += " fc-state-highlight";
37087                 
37088             }
37089             // disabling
37090             if(t < min) {
37091                 //cell.className = " fc-state-disabled";
37092                 cell.title = cal.minText;
37093                 return;
37094             }
37095             if(t > max) {
37096                 //cell.className = " fc-state-disabled";
37097                 cell.title = cal.maxText;
37098                 return;
37099             }
37100             if(ddays){
37101                 if(ddays.indexOf(d.getDay()) != -1){
37102                     // cell.title = ddaysText;
37103                    // cell.className = " fc-state-disabled";
37104                 }
37105             }
37106             if(ddMatch && format){
37107                 var fvalue = d.dateFormat(format);
37108                 if(ddMatch.test(fvalue)){
37109                     cell.title = ddText.replace("%0", fvalue);
37110                    cell.className = " fc-state-disabled";
37111                 }
37112             }
37113             
37114             if (!cell.initialClassName) {
37115                 cell.initialClassName = cell.dom.className;
37116             }
37117             
37118             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
37119         };
37120
37121         var i = 0;
37122         
37123         for(; i < startingPos; i++) {
37124             cells[i].dayName =  (++prevStart);
37125             Roo.log(textEls[i]);
37126             d.setDate(d.getDate()+1);
37127             
37128             //cells[i].className = "fc-past fc-other-month";
37129             setCellClass(this, cells[i]);
37130         }
37131         
37132         var intDay = 0;
37133         
37134         for(; i < days; i++){
37135             intDay = i - startingPos + 1;
37136             cells[i].dayName =  (intDay);
37137             d.setDate(d.getDate()+1);
37138             
37139             cells[i].className = ''; // "x-date-active";
37140             setCellClass(this, cells[i]);
37141         }
37142         var extraDays = 0;
37143         
37144         for(; i < 42; i++) {
37145             //textEls[i].innerHTML = (++extraDays);
37146             
37147             d.setDate(d.getDate()+1);
37148             cells[i].dayName = (++extraDays);
37149             cells[i].className = "fc-future fc-other-month";
37150             setCellClass(this, cells[i]);
37151         }
37152         
37153         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37154         
37155         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37156         
37157         // this will cause all the cells to mis
37158         var rows= [];
37159         var i =0;
37160         for (var r = 0;r < 6;r++) {
37161             for (var c =0;c < 7;c++) {
37162                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37163             }    
37164         }
37165         
37166         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37167         for(i=0;i<cells.length;i++) {
37168             
37169             this.cells.elements[i].dayName = cells[i].dayName ;
37170             this.cells.elements[i].className = cells[i].className;
37171             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37172             this.cells.elements[i].title = cells[i].title ;
37173             this.cells.elements[i].dateValue = cells[i].dateValue ;
37174         }
37175         
37176         
37177         
37178         
37179         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37180         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37181         
37182         ////if(totalRows != 6){
37183             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37184            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37185        // }
37186         
37187         this.fireEvent('monthchange', this, date);
37188         
37189         
37190     },
37191  /**
37192      * Returns the grid's SelectionModel.
37193      * @return {SelectionModel}
37194      */
37195     getSelectionModel : function(){
37196         if(!this.selModel){
37197             this.selModel = new Roo.grid.CellSelectionModel();
37198         }
37199         return this.selModel;
37200     },
37201
37202     load: function() {
37203         this.eventStore.load()
37204         
37205         
37206         
37207     },
37208     
37209     findCell : function(dt) {
37210         dt = dt.clearTime().getTime();
37211         var ret = false;
37212         this.cells.each(function(c){
37213             //Roo.log("check " +c.dateValue + '?=' + dt);
37214             if(c.dateValue == dt){
37215                 ret = c;
37216                 return false;
37217             }
37218             return true;
37219         });
37220         
37221         return ret;
37222     },
37223     
37224     findCells : function(rec) {
37225         var s = rec.data.start_dt.clone().clearTime().getTime();
37226        // Roo.log(s);
37227         var e= rec.data.end_dt.clone().clearTime().getTime();
37228        // Roo.log(e);
37229         var ret = [];
37230         this.cells.each(function(c){
37231              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37232             
37233             if(c.dateValue > e){
37234                 return ;
37235             }
37236             if(c.dateValue < s){
37237                 return ;
37238             }
37239             ret.push(c);
37240         });
37241         
37242         return ret;    
37243     },
37244     
37245     findBestRow: function(cells)
37246     {
37247         var ret = 0;
37248         
37249         for (var i =0 ; i < cells.length;i++) {
37250             ret  = Math.max(cells[i].rows || 0,ret);
37251         }
37252         return ret;
37253         
37254     },
37255     
37256     
37257     addItem : function(rec)
37258     {
37259         // look for vertical location slot in
37260         var cells = this.findCells(rec);
37261         
37262         rec.row = this.findBestRow(cells);
37263         
37264         // work out the location.
37265         
37266         var crow = false;
37267         var rows = [];
37268         for(var i =0; i < cells.length; i++) {
37269             if (!crow) {
37270                 crow = {
37271                     start : cells[i],
37272                     end :  cells[i]
37273                 };
37274                 continue;
37275             }
37276             if (crow.start.getY() == cells[i].getY()) {
37277                 // on same row.
37278                 crow.end = cells[i];
37279                 continue;
37280             }
37281             // different row.
37282             rows.push(crow);
37283             crow = {
37284                 start: cells[i],
37285                 end : cells[i]
37286             };
37287             
37288         }
37289         
37290         rows.push(crow);
37291         rec.els = [];
37292         rec.rows = rows;
37293         rec.cells = cells;
37294         for (var i = 0; i < cells.length;i++) {
37295             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
37296             
37297         }
37298         
37299         
37300     },
37301     
37302     clearEvents: function() {
37303         
37304         if (!this.eventStore.getCount()) {
37305             return;
37306         }
37307         // reset number of rows in cells.
37308         Roo.each(this.cells.elements, function(c){
37309             c.rows = 0;
37310         });
37311         
37312         this.eventStore.each(function(e) {
37313             this.clearEvent(e);
37314         },this);
37315         
37316     },
37317     
37318     clearEvent : function(ev)
37319     {
37320         if (ev.els) {
37321             Roo.each(ev.els, function(el) {
37322                 el.un('mouseenter' ,this.onEventEnter, this);
37323                 el.un('mouseleave' ,this.onEventLeave, this);
37324                 el.remove();
37325             },this);
37326             ev.els = [];
37327         }
37328     },
37329     
37330     
37331     renderEvent : function(ev,ctr) {
37332         if (!ctr) {
37333              ctr = this.view.el.select('.fc-event-container',true).first();
37334         }
37335         
37336          
37337         this.clearEvent(ev);
37338             //code
37339        
37340         
37341         
37342         ev.els = [];
37343         var cells = ev.cells;
37344         var rows = ev.rows;
37345         this.fireEvent('eventrender', this, ev);
37346         
37347         for(var i =0; i < rows.length; i++) {
37348             
37349             cls = '';
37350             if (i == 0) {
37351                 cls += ' fc-event-start';
37352             }
37353             if ((i+1) == rows.length) {
37354                 cls += ' fc-event-end';
37355             }
37356             
37357             //Roo.log(ev.data);
37358             // how many rows should it span..
37359             var cg = this.eventTmpl.append(ctr,Roo.apply({
37360                 fccls : cls
37361                 
37362             }, ev.data) , true);
37363             
37364             
37365             cg.on('mouseenter' ,this.onEventEnter, this, ev);
37366             cg.on('mouseleave' ,this.onEventLeave, this, ev);
37367             cg.on('click', this.onEventClick, this, ev);
37368             
37369             ev.els.push(cg);
37370             
37371             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
37372             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
37373             //Roo.log(cg);
37374              
37375             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
37376             cg.setWidth(ebox.right - sbox.x -2);
37377         }
37378     },
37379     
37380     renderEvents: function()
37381     {   
37382         // first make sure there is enough space..
37383         
37384         if (!this.eventTmpl) {
37385             this.eventTmpl = new Roo.Template(
37386                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
37387                     '<div class="fc-event-inner">' +
37388                         '<span class="fc-event-time">{time}</span>' +
37389                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
37390                     '</div>' +
37391                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
37392                 '</div>'
37393             );
37394                 
37395         }
37396                
37397         
37398         
37399         this.cells.each(function(c) {
37400             //Roo.log(c.select('.fc-day-content div',true).first());
37401             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
37402         });
37403         
37404         var ctr = this.view.el.select('.fc-event-container',true).first();
37405         
37406         var cls;
37407         this.eventStore.each(function(ev){
37408             
37409             this.renderEvent(ev);
37410              
37411              
37412         }, this);
37413         this.view.layout();
37414         
37415     },
37416     
37417     onEventEnter: function (e, el,event,d) {
37418         this.fireEvent('evententer', this, el, event);
37419     },
37420     
37421     onEventLeave: function (e, el,event,d) {
37422         this.fireEvent('eventleave', this, el, event);
37423     },
37424     
37425     onEventClick: function (e, el,event,d) {
37426         this.fireEvent('eventclick', this, el, event);
37427     },
37428     
37429     onMonthChange: function () {
37430         this.store.load();
37431     },
37432     
37433     onLoad: function () {
37434         
37435         //Roo.log('calendar onload');
37436 //         
37437         if(this.eventStore.getCount() > 0){
37438             
37439            
37440             
37441             this.eventStore.each(function(d){
37442                 
37443                 
37444                 // FIXME..
37445                 var add =   d.data;
37446                 if (typeof(add.end_dt) == 'undefined')  {
37447                     Roo.log("Missing End time in calendar data: ");
37448                     Roo.log(d);
37449                     return;
37450                 }
37451                 if (typeof(add.start_dt) == 'undefined')  {
37452                     Roo.log("Missing Start time in calendar data: ");
37453                     Roo.log(d);
37454                     return;
37455                 }
37456                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
37457                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
37458                 add.id = add.id || d.id;
37459                 add.title = add.title || '??';
37460                 
37461                 this.addItem(d);
37462                 
37463              
37464             },this);
37465         }
37466         
37467         this.renderEvents();
37468     }
37469     
37470
37471 });
37472 /*
37473  grid : {
37474                 xtype: 'Grid',
37475                 xns: Roo.grid,
37476                 listeners : {
37477                     render : function ()
37478                     {
37479                         _this.grid = this;
37480                         
37481                         if (!this.view.el.hasClass('course-timesheet')) {
37482                             this.view.el.addClass('course-timesheet');
37483                         }
37484                         if (this.tsStyle) {
37485                             this.ds.load({});
37486                             return; 
37487                         }
37488                         Roo.log('width');
37489                         Roo.log(_this.grid.view.el.getWidth());
37490                         
37491                         
37492                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
37493                             '.course-timesheet .x-grid-row' : {
37494                                 height: '80px'
37495                             },
37496                             '.x-grid-row td' : {
37497                                 'vertical-align' : 0
37498                             },
37499                             '.course-edit-link' : {
37500                                 'color' : 'blue',
37501                                 'text-overflow' : 'ellipsis',
37502                                 'overflow' : 'hidden',
37503                                 'white-space' : 'nowrap',
37504                                 'cursor' : 'pointer'
37505                             },
37506                             '.sub-link' : {
37507                                 'color' : 'green'
37508                             },
37509                             '.de-act-sup-link' : {
37510                                 'color' : 'purple',
37511                                 'text-decoration' : 'line-through'
37512                             },
37513                             '.de-act-link' : {
37514                                 'color' : 'red',
37515                                 'text-decoration' : 'line-through'
37516                             },
37517                             '.course-timesheet .course-highlight' : {
37518                                 'border-top-style': 'dashed !important',
37519                                 'border-bottom-bottom': 'dashed !important'
37520                             },
37521                             '.course-timesheet .course-item' : {
37522                                 'font-family'   : 'tahoma, arial, helvetica',
37523                                 'font-size'     : '11px',
37524                                 'overflow'      : 'hidden',
37525                                 'padding-left'  : '10px',
37526                                 'padding-right' : '10px',
37527                                 'padding-top' : '10px' 
37528                             }
37529                             
37530                         }, Roo.id());
37531                                 this.ds.load({});
37532                     }
37533                 },
37534                 autoWidth : true,
37535                 monitorWindowResize : false,
37536                 cellrenderer : function(v,x,r)
37537                 {
37538                     return v;
37539                 },
37540                 sm : {
37541                     xtype: 'CellSelectionModel',
37542                     xns: Roo.grid
37543                 },
37544                 dataSource : {
37545                     xtype: 'Store',
37546                     xns: Roo.data,
37547                     listeners : {
37548                         beforeload : function (_self, options)
37549                         {
37550                             options.params = options.params || {};
37551                             options.params._month = _this.monthField.getValue();
37552                             options.params.limit = 9999;
37553                             options.params['sort'] = 'when_dt';    
37554                             options.params['dir'] = 'ASC';    
37555                             this.proxy.loadResponse = this.loadResponse;
37556                             Roo.log("load?");
37557                             //this.addColumns();
37558                         },
37559                         load : function (_self, records, options)
37560                         {
37561                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
37562                                 // if you click on the translation.. you can edit it...
37563                                 var el = Roo.get(this);
37564                                 var id = el.dom.getAttribute('data-id');
37565                                 var d = el.dom.getAttribute('data-date');
37566                                 var t = el.dom.getAttribute('data-time');
37567                                 //var id = this.child('span').dom.textContent;
37568                                 
37569                                 //Roo.log(this);
37570                                 Pman.Dialog.CourseCalendar.show({
37571                                     id : id,
37572                                     when_d : d,
37573                                     when_t : t,
37574                                     productitem_active : id ? 1 : 0
37575                                 }, function() {
37576                                     _this.grid.ds.load({});
37577                                 });
37578                            
37579                            });
37580                            
37581                            _this.panel.fireEvent('resize', [ '', '' ]);
37582                         }
37583                     },
37584                     loadResponse : function(o, success, response){
37585                             // this is overridden on before load..
37586                             
37587                             Roo.log("our code?");       
37588                             //Roo.log(success);
37589                             //Roo.log(response)
37590                             delete this.activeRequest;
37591                             if(!success){
37592                                 this.fireEvent("loadexception", this, o, response);
37593                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
37594                                 return;
37595                             }
37596                             var result;
37597                             try {
37598                                 result = o.reader.read(response);
37599                             }catch(e){
37600                                 Roo.log("load exception?");
37601                                 this.fireEvent("loadexception", this, o, response, e);
37602                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
37603                                 return;
37604                             }
37605                             Roo.log("ready...");        
37606                             // loop through result.records;
37607                             // and set this.tdate[date] = [] << array of records..
37608                             _this.tdata  = {};
37609                             Roo.each(result.records, function(r){
37610                                 //Roo.log(r.data);
37611                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
37612                                     _this.tdata[r.data.when_dt.format('j')] = [];
37613                                 }
37614                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
37615                             });
37616                             
37617                             //Roo.log(_this.tdata);
37618                             
37619                             result.records = [];
37620                             result.totalRecords = 6;
37621                     
37622                             // let's generate some duumy records for the rows.
37623                             //var st = _this.dateField.getValue();
37624                             
37625                             // work out monday..
37626                             //st = st.add(Date.DAY, -1 * st.format('w'));
37627                             
37628                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37629                             
37630                             var firstOfMonth = date.getFirstDayOfMonth();
37631                             var days = date.getDaysInMonth();
37632                             var d = 1;
37633                             var firstAdded = false;
37634                             for (var i = 0; i < result.totalRecords ; i++) {
37635                                 //var d= st.add(Date.DAY, i);
37636                                 var row = {};
37637                                 var added = 0;
37638                                 for(var w = 0 ; w < 7 ; w++){
37639                                     if(!firstAdded && firstOfMonth != w){
37640                                         continue;
37641                                     }
37642                                     if(d > days){
37643                                         continue;
37644                                     }
37645                                     firstAdded = true;
37646                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
37647                                     row['weekday'+w] = String.format(
37648                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
37649                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
37650                                                     d,
37651                                                     date.format('Y-m-')+dd
37652                                                 );
37653                                     added++;
37654                                     if(typeof(_this.tdata[d]) != 'undefined'){
37655                                         Roo.each(_this.tdata[d], function(r){
37656                                             var is_sub = '';
37657                                             var deactive = '';
37658                                             var id = r.id;
37659                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
37660                                             if(r.parent_id*1>0){
37661                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
37662                                                 id = r.parent_id;
37663                                             }
37664                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
37665                                                 deactive = 'de-act-link';
37666                                             }
37667                                             
37668                                             row['weekday'+w] += String.format(
37669                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
37670                                                     id, //0
37671                                                     r.product_id_name, //1
37672                                                     r.when_dt.format('h:ia'), //2
37673                                                     is_sub, //3
37674                                                     deactive, //4
37675                                                     desc // 5
37676                                             );
37677                                         });
37678                                     }
37679                                     d++;
37680                                 }
37681                                 
37682                                 // only do this if something added..
37683                                 if(added > 0){ 
37684                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
37685                                 }
37686                                 
37687                                 
37688                                 // push it twice. (second one with an hour..
37689                                 
37690                             }
37691                             //Roo.log(result);
37692                             this.fireEvent("load", this, o, o.request.arg);
37693                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
37694                         },
37695                     sortInfo : {field: 'when_dt', direction : 'ASC' },
37696                     proxy : {
37697                         xtype: 'HttpProxy',
37698                         xns: Roo.data,
37699                         method : 'GET',
37700                         url : baseURL + '/Roo/Shop_course.php'
37701                     },
37702                     reader : {
37703                         xtype: 'JsonReader',
37704                         xns: Roo.data,
37705                         id : 'id',
37706                         fields : [
37707                             {
37708                                 'name': 'id',
37709                                 'type': 'int'
37710                             },
37711                             {
37712                                 'name': 'when_dt',
37713                                 'type': 'string'
37714                             },
37715                             {
37716                                 'name': 'end_dt',
37717                                 'type': 'string'
37718                             },
37719                             {
37720                                 'name': 'parent_id',
37721                                 'type': 'int'
37722                             },
37723                             {
37724                                 'name': 'product_id',
37725                                 'type': 'int'
37726                             },
37727                             {
37728                                 'name': 'productitem_id',
37729                                 'type': 'int'
37730                             },
37731                             {
37732                                 'name': 'guid',
37733                                 'type': 'int'
37734                             }
37735                         ]
37736                     }
37737                 },
37738                 toolbar : {
37739                     xtype: 'Toolbar',
37740                     xns: Roo,
37741                     items : [
37742                         {
37743                             xtype: 'Button',
37744                             xns: Roo.Toolbar,
37745                             listeners : {
37746                                 click : function (_self, e)
37747                                 {
37748                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37749                                     sd.setMonth(sd.getMonth()-1);
37750                                     _this.monthField.setValue(sd.format('Y-m-d'));
37751                                     _this.grid.ds.load({});
37752                                 }
37753                             },
37754                             text : "Back"
37755                         },
37756                         {
37757                             xtype: 'Separator',
37758                             xns: Roo.Toolbar
37759                         },
37760                         {
37761                             xtype: 'MonthField',
37762                             xns: Roo.form,
37763                             listeners : {
37764                                 render : function (_self)
37765                                 {
37766                                     _this.monthField = _self;
37767                                    // _this.monthField.set  today
37768                                 },
37769                                 select : function (combo, date)
37770                                 {
37771                                     _this.grid.ds.load({});
37772                                 }
37773                             },
37774                             value : (function() { return new Date(); })()
37775                         },
37776                         {
37777                             xtype: 'Separator',
37778                             xns: Roo.Toolbar
37779                         },
37780                         {
37781                             xtype: 'TextItem',
37782                             xns: Roo.Toolbar,
37783                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
37784                         },
37785                         {
37786                             xtype: 'Fill',
37787                             xns: Roo.Toolbar
37788                         },
37789                         {
37790                             xtype: 'Button',
37791                             xns: Roo.Toolbar,
37792                             listeners : {
37793                                 click : function (_self, e)
37794                                 {
37795                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37796                                     sd.setMonth(sd.getMonth()+1);
37797                                     _this.monthField.setValue(sd.format('Y-m-d'));
37798                                     _this.grid.ds.load({});
37799                                 }
37800                             },
37801                             text : "Next"
37802                         }
37803                     ]
37804                 },
37805                  
37806             }
37807         };
37808         
37809         *//*
37810  * Based on:
37811  * Ext JS Library 1.1.1
37812  * Copyright(c) 2006-2007, Ext JS, LLC.
37813  *
37814  * Originally Released Under LGPL - original licence link has changed is not relivant.
37815  *
37816  * Fork - LGPL
37817  * <script type="text/javascript">
37818  */
37819  
37820 /**
37821  * @class Roo.LoadMask
37822  * A simple utility class for generically masking elements while loading data.  If the element being masked has
37823  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
37824  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
37825  * element's UpdateManager load indicator and will be destroyed after the initial load.
37826  * @constructor
37827  * Create a new LoadMask
37828  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
37829  * @param {Object} config The config object
37830  */
37831 Roo.LoadMask = function(el, config){
37832     this.el = Roo.get(el);
37833     Roo.apply(this, config);
37834     if(this.store){
37835         this.store.on('beforeload', this.onBeforeLoad, this);
37836         this.store.on('load', this.onLoad, this);
37837         this.store.on('loadexception', this.onLoadException, this);
37838         this.removeMask = false;
37839     }else{
37840         var um = this.el.getUpdateManager();
37841         um.showLoadIndicator = false; // disable the default indicator
37842         um.on('beforeupdate', this.onBeforeLoad, this);
37843         um.on('update', this.onLoad, this);
37844         um.on('failure', this.onLoad, this);
37845         this.removeMask = true;
37846     }
37847 };
37848
37849 Roo.LoadMask.prototype = {
37850     /**
37851      * @cfg {Boolean} removeMask
37852      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
37853      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
37854      */
37855     /**
37856      * @cfg {String} msg
37857      * The text to display in a centered loading message box (defaults to 'Loading...')
37858      */
37859     msg : 'Loading...',
37860     /**
37861      * @cfg {String} msgCls
37862      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
37863      */
37864     msgCls : 'x-mask-loading',
37865
37866     /**
37867      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
37868      * @type Boolean
37869      */
37870     disabled: false,
37871
37872     /**
37873      * Disables the mask to prevent it from being displayed
37874      */
37875     disable : function(){
37876        this.disabled = true;
37877     },
37878
37879     /**
37880      * Enables the mask so that it can be displayed
37881      */
37882     enable : function(){
37883         this.disabled = false;
37884     },
37885     
37886     onLoadException : function()
37887     {
37888         Roo.log(arguments);
37889         
37890         if (typeof(arguments[3]) != 'undefined') {
37891             Roo.MessageBox.alert("Error loading",arguments[3]);
37892         } 
37893         /*
37894         try {
37895             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
37896                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
37897             }   
37898         } catch(e) {
37899             
37900         }
37901         */
37902     
37903         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
37904     },
37905     // private
37906     onLoad : function()
37907     {
37908         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
37909     },
37910
37911     // private
37912     onBeforeLoad : function(){
37913         if(!this.disabled){
37914             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
37915         }
37916     },
37917
37918     // private
37919     destroy : function(){
37920         if(this.store){
37921             this.store.un('beforeload', this.onBeforeLoad, this);
37922             this.store.un('load', this.onLoad, this);
37923             this.store.un('loadexception', this.onLoadException, this);
37924         }else{
37925             var um = this.el.getUpdateManager();
37926             um.un('beforeupdate', this.onBeforeLoad, this);
37927             um.un('update', this.onLoad, this);
37928             um.un('failure', this.onLoad, this);
37929         }
37930     }
37931 };/*
37932  * Based on:
37933  * Ext JS Library 1.1.1
37934  * Copyright(c) 2006-2007, Ext JS, LLC.
37935  *
37936  * Originally Released Under LGPL - original licence link has changed is not relivant.
37937  *
37938  * Fork - LGPL
37939  * <script type="text/javascript">
37940  */
37941
37942
37943 /**
37944  * @class Roo.XTemplate
37945  * @extends Roo.Template
37946  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
37947 <pre><code>
37948 var t = new Roo.XTemplate(
37949         '&lt;select name="{name}"&gt;',
37950                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
37951         '&lt;/select&gt;'
37952 );
37953  
37954 // then append, applying the master template values
37955  </code></pre>
37956  *
37957  * Supported features:
37958  *
37959  *  Tags:
37960
37961 <pre><code>
37962       {a_variable} - output encoded.
37963       {a_variable.format:("Y-m-d")} - call a method on the variable
37964       {a_variable:raw} - unencoded output
37965       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
37966       {a_variable:this.method_on_template(...)} - call a method on the template object.
37967  
37968 </code></pre>
37969  *  The tpl tag:
37970 <pre><code>
37971         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
37972         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
37973         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
37974         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
37975   
37976         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
37977         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
37978 </code></pre>
37979  *      
37980  */
37981 Roo.XTemplate = function()
37982 {
37983     Roo.XTemplate.superclass.constructor.apply(this, arguments);
37984     if (this.html) {
37985         this.compile();
37986     }
37987 };
37988
37989
37990 Roo.extend(Roo.XTemplate, Roo.Template, {
37991
37992     /**
37993      * The various sub templates
37994      */
37995     tpls : false,
37996     /**
37997      *
37998      * basic tag replacing syntax
37999      * WORD:WORD()
38000      *
38001      * // you can fake an object call by doing this
38002      *  x.t:(test,tesT) 
38003      * 
38004      */
38005     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38006
38007     /**
38008      * compile the template
38009      *
38010      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38011      *
38012      */
38013     compile: function()
38014     {
38015         var s = this.html;
38016      
38017         s = ['<tpl>', s, '</tpl>'].join('');
38018     
38019         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38020             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38021             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38022             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38023             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38024             m,
38025             id     = 0,
38026             tpls   = [];
38027     
38028         while(true == !!(m = s.match(re))){
38029             var forMatch   = m[0].match(nameRe),
38030                 ifMatch   = m[0].match(ifRe),
38031                 execMatch   = m[0].match(execRe),
38032                 namedMatch   = m[0].match(namedRe),
38033                 
38034                 exp  = null, 
38035                 fn   = null,
38036                 exec = null,
38037                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38038                 
38039             if (ifMatch) {
38040                 // if - puts fn into test..
38041                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38042                 if(exp){
38043                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38044                 }
38045             }
38046             
38047             if (execMatch) {
38048                 // exec - calls a function... returns empty if true is  returned.
38049                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38050                 if(exp){
38051                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38052                 }
38053             }
38054             
38055             
38056             if (name) {
38057                 // for = 
38058                 switch(name){
38059                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38060                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38061                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38062                 }
38063             }
38064             var uid = namedMatch ? namedMatch[1] : id;
38065             
38066             
38067             tpls.push({
38068                 id:     namedMatch ? namedMatch[1] : id,
38069                 target: name,
38070                 exec:   exec,
38071                 test:   fn,
38072                 body:   m[1] || ''
38073             });
38074             if (namedMatch) {
38075                 s = s.replace(m[0], '');
38076             } else { 
38077                 s = s.replace(m[0], '{xtpl'+ id + '}');
38078             }
38079             ++id;
38080         }
38081         this.tpls = [];
38082         for(var i = tpls.length-1; i >= 0; --i){
38083             this.compileTpl(tpls[i]);
38084             this.tpls[tpls[i].id] = tpls[i];
38085         }
38086         this.master = tpls[tpls.length-1];
38087         return this;
38088     },
38089     /**
38090      * same as applyTemplate, except it's done to one of the subTemplates
38091      * when using named templates, you can do:
38092      *
38093      * var str = pl.applySubTemplate('your-name', values);
38094      *
38095      * 
38096      * @param {Number} id of the template
38097      * @param {Object} values to apply to template
38098      * @param {Object} parent (normaly the instance of this object)
38099      */
38100     applySubTemplate : function(id, values, parent)
38101     {
38102         
38103         
38104         var t = this.tpls[id];
38105         
38106         
38107         try { 
38108             if(t.test && !t.test.call(this, values, parent)){
38109                 return '';
38110             }
38111         } catch(e) {
38112             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38113             Roo.log(e.toString());
38114             Roo.log(t.test);
38115             return ''
38116         }
38117         try { 
38118             
38119             if(t.exec && t.exec.call(this, values, parent)){
38120                 return '';
38121             }
38122         } catch(e) {
38123             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38124             Roo.log(e.toString());
38125             Roo.log(t.exec);
38126             return ''
38127         }
38128         try {
38129             var vs = t.target ? t.target.call(this, values, parent) : values;
38130             parent = t.target ? values : parent;
38131             if(t.target && vs instanceof Array){
38132                 var buf = [];
38133                 for(var i = 0, len = vs.length; i < len; i++){
38134                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38135                 }
38136                 return buf.join('');
38137             }
38138             return t.compiled.call(this, vs, parent);
38139         } catch (e) {
38140             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38141             Roo.log(e.toString());
38142             Roo.log(t.compiled);
38143             return '';
38144         }
38145     },
38146
38147     compileTpl : function(tpl)
38148     {
38149         var fm = Roo.util.Format;
38150         var useF = this.disableFormats !== true;
38151         var sep = Roo.isGecko ? "+" : ",";
38152         var undef = function(str) {
38153             Roo.log("Property not found :"  + str);
38154             return '';
38155         };
38156         
38157         var fn = function(m, name, format, args)
38158         {
38159             //Roo.log(arguments);
38160             args = args ? args.replace(/\\'/g,"'") : args;
38161             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38162             if (typeof(format) == 'undefined') {
38163                 format= 'htmlEncode';
38164             }
38165             if (format == 'raw' ) {
38166                 format = false;
38167             }
38168             
38169             if(name.substr(0, 4) == 'xtpl'){
38170                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38171             }
38172             
38173             // build an array of options to determine if value is undefined..
38174             
38175             // basically get 'xxxx.yyyy' then do
38176             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38177             //    (function () { Roo.log("Property not found"); return ''; })() :
38178             //    ......
38179             
38180             var udef_ar = [];
38181             var lookfor = '';
38182             Roo.each(name.split('.'), function(st) {
38183                 lookfor += (lookfor.length ? '.': '') + st;
38184                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38185             });
38186             
38187             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38188             
38189             
38190             if(format && useF){
38191                 
38192                 args = args ? ',' + args : "";
38193                  
38194                 if(format.substr(0, 5) != "this."){
38195                     format = "fm." + format + '(';
38196                 }else{
38197                     format = 'this.call("'+ format.substr(5) + '", ';
38198                     args = ", values";
38199                 }
38200                 
38201                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38202             }
38203              
38204             if (args.length) {
38205                 // called with xxyx.yuu:(test,test)
38206                 // change to ()
38207                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38208             }
38209             // raw.. - :raw modifier..
38210             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38211             
38212         };
38213         var body;
38214         // branched to use + in gecko and [].join() in others
38215         if(Roo.isGecko){
38216             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38217                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38218                     "';};};";
38219         }else{
38220             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38221             body.push(tpl.body.replace(/(\r\n|\n)/g,
38222                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38223             body.push("'].join('');};};");
38224             body = body.join('');
38225         }
38226         
38227         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38228        
38229         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38230         eval(body);
38231         
38232         return this;
38233     },
38234
38235     applyTemplate : function(values){
38236         return this.master.compiled.call(this, values, {});
38237         //var s = this.subs;
38238     },
38239
38240     apply : function(){
38241         return this.applyTemplate.apply(this, arguments);
38242     }
38243
38244  });
38245
38246 Roo.XTemplate.from = function(el){
38247     el = Roo.getDom(el);
38248     return new Roo.XTemplate(el.value || el.innerHTML);
38249 };