roojs-ui.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13 /**
14  * @class Roo.data.SortTypes
15  * @singleton
16  * Defines the default sorting (casting?) comparison functions used when sorting data.
17  */
18 Roo.data.SortTypes = {
19     /**
20      * Default sort that does nothing
21      * @param {Mixed} s The value being converted
22      * @return {Mixed} The comparison value
23      */
24     none : function(s){
25         return s;
26     },
27     
28     /**
29      * The regular expression used to strip tags
30      * @type {RegExp}
31      * @property
32      */
33     stripTagsRE : /<\/?[^>]+>/gi,
34     
35     /**
36      * Strips all HTML tags to sort on text only
37      * @param {Mixed} s The value being converted
38      * @return {String} The comparison value
39      */
40     asText : function(s){
41         return String(s).replace(this.stripTagsRE, "");
42     },
43     
44     /**
45      * Strips all HTML tags to sort on text only - Case insensitive
46      * @param {Mixed} s The value being converted
47      * @return {String} The comparison value
48      */
49     asUCText : function(s){
50         return String(s).toUpperCase().replace(this.stripTagsRE, "");
51     },
52     
53     /**
54      * Case insensitive string
55      * @param {Mixed} s The value being converted
56      * @return {String} The comparison value
57      */
58     asUCString : function(s) {
59         return String(s).toUpperCase();
60     },
61     
62     /**
63      * Date sorting
64      * @param {Mixed} s The value being converted
65      * @return {Number} The comparison value
66      */
67     asDate : function(s) {
68         if(!s){
69             return 0;
70         }
71         if(s instanceof Date){
72             return s.getTime();
73         }
74         return Date.parse(String(s));
75     },
76     
77     /**
78      * Float sorting
79      * @param {Mixed} s The value being converted
80      * @return {Float} The comparison value
81      */
82     asFloat : function(s) {
83         var val = parseFloat(String(s).replace(/,/g, ""));
84         if(isNaN(val)) {
85             val = 0;
86         }
87         return val;
88     },
89     
90     /**
91      * Integer sorting
92      * @param {Mixed} s The value being converted
93      * @return {Number} The comparison value
94      */
95     asInt : function(s) {
96         var val = parseInt(String(s).replace(/,/g, ""));
97         if(isNaN(val)) {
98             val = 0;
99         }
100         return val;
101     }
102 };/*
103  * Based on:
104  * Ext JS Library 1.1.1
105  * Copyright(c) 2006-2007, Ext JS, LLC.
106  *
107  * Originally Released Under LGPL - original licence link has changed is not relivant.
108  *
109  * Fork - LGPL
110  * <script type="text/javascript">
111  */
112
113 /**
114 * @class Roo.data.Record
115  * Instances of this class encapsulate both record <em>definition</em> information, and record
116  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
117  * to access Records cached in an {@link Roo.data.Store} object.<br>
118  * <p>
119  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
120  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
121  * objects.<br>
122  * <p>
123  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
124  * @constructor
125  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
126  * {@link #create}. The parameters are the same.
127  * @param {Array} data An associative Array of data values keyed by the field name.
128  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
129  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
130  * not specified an integer id is generated.
131  */
132 Roo.data.Record = function(data, id){
133     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
134     this.data = data;
135 };
136
137 /**
138  * Generate a constructor for a specific record layout.
139  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
140  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
141  * Each field definition object may contain the following properties: <ul>
142  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
143  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
144  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
145  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
146  * is being used, then this is a string containing the javascript expression to reference the data relative to 
147  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
148  * to the data item relative to the record element. If the mapping expression is the same as the field name,
149  * this may be omitted.</p></li>
150  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
151  * <ul><li>auto (Default, implies no conversion)</li>
152  * <li>string</li>
153  * <li>int</li>
154  * <li>float</li>
155  * <li>boolean</li>
156  * <li>date</li></ul></p></li>
157  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
158  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
159  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
160  * by the Reader into an object that will be stored in the Record. It is passed the
161  * following parameters:<ul>
162  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
163  * </ul></p></li>
164  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
165  * </ul>
166  * <br>usage:<br><pre><code>
167 var TopicRecord = Roo.data.Record.create(
168     {name: 'title', mapping: 'topic_title'},
169     {name: 'author', mapping: 'username'},
170     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
171     {name: 'lastPost', mapping: 'post_time', type: 'date'},
172     {name: 'lastPoster', mapping: 'user2'},
173     {name: 'excerpt', mapping: 'post_text'}
174 );
175
176 var myNewRecord = new TopicRecord({
177     title: 'Do my job please',
178     author: 'noobie',
179     totalPosts: 1,
180     lastPost: new Date(),
181     lastPoster: 'Animal',
182     excerpt: 'No way dude!'
183 });
184 myStore.add(myNewRecord);
185 </code></pre>
186  * @method create
187  * @static
188  */
189 Roo.data.Record.create = function(o){
190     var f = function(){
191         f.superclass.constructor.apply(this, arguments);
192     };
193     Roo.extend(f, Roo.data.Record);
194     var p = f.prototype;
195     p.fields = new Roo.util.MixedCollection(false, function(field){
196         return field.name;
197     });
198     for(var i = 0, len = o.length; i < len; i++){
199         p.fields.add(new Roo.data.Field(o[i]));
200     }
201     f.getField = function(name){
202         return p.fields.get(name);  
203     };
204     return f;
205 };
206
207 Roo.data.Record.AUTO_ID = 1000;
208 Roo.data.Record.EDIT = 'edit';
209 Roo.data.Record.REJECT = 'reject';
210 Roo.data.Record.COMMIT = 'commit';
211
212 Roo.data.Record.prototype = {
213     /**
214      * Readonly flag - true if this record has been modified.
215      * @type Boolean
216      */
217     dirty : false,
218     editing : false,
219     error: null,
220     modified: null,
221
222     // private
223     join : function(store){
224         this.store = store;
225     },
226
227     /**
228      * Set the named field to the specified value.
229      * @param {String} name The name of the field to set.
230      * @param {Object} value The value to set the field to.
231      */
232     set : function(name, value){
233         if(this.data[name] == value){
234             return;
235         }
236         this.dirty = true;
237         if(!this.modified){
238             this.modified = {};
239         }
240         if(typeof this.modified[name] == 'undefined'){
241             this.modified[name] = this.data[name];
242         }
243         this.data[name] = value;
244         if(!this.editing && this.store){
245             this.store.afterEdit(this);
246         }       
247     },
248
249     /**
250      * Get the value of the named field.
251      * @param {String} name The name of the field to get the value of.
252      * @return {Object} The value of the field.
253      */
254     get : function(name){
255         return this.data[name]; 
256     },
257
258     // private
259     beginEdit : function(){
260         this.editing = true;
261         this.modified = {}; 
262     },
263
264     // private
265     cancelEdit : function(){
266         this.editing = false;
267         delete this.modified;
268     },
269
270     // private
271     endEdit : function(){
272         this.editing = false;
273         if(this.dirty && this.store){
274             this.store.afterEdit(this);
275         }
276     },
277
278     /**
279      * Usually called by the {@link Roo.data.Store} which owns the Record.
280      * Rejects all changes made to the Record since either creation, or the last commit operation.
281      * Modified fields are reverted to their original values.
282      * <p>
283      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
284      * of reject operations.
285      */
286     reject : function(){
287         var m = this.modified;
288         for(var n in m){
289             if(typeof m[n] != "function"){
290                 this.data[n] = m[n];
291             }
292         }
293         this.dirty = false;
294         delete this.modified;
295         this.editing = false;
296         if(this.store){
297             this.store.afterReject(this);
298         }
299     },
300
301     /**
302      * Usually called by the {@link Roo.data.Store} which owns the Record.
303      * Commits all changes made to the Record since either creation, or the last commit operation.
304      * <p>
305      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
306      * of commit operations.
307      */
308     commit : function(){
309         this.dirty = false;
310         delete this.modified;
311         this.editing = false;
312         if(this.store){
313             this.store.afterCommit(this);
314         }
315     },
316
317     // private
318     hasError : function(){
319         return this.error != null;
320     },
321
322     // private
323     clearError : function(){
324         this.error = null;
325     },
326
327     /**
328      * Creates a copy of this record.
329      * @param {String} id (optional) A new record id if you don't want to use this record's id
330      * @return {Record}
331      */
332     copy : function(newId) {
333         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
334     }
335 };/*
336  * Based on:
337  * Ext JS Library 1.1.1
338  * Copyright(c) 2006-2007, Ext JS, LLC.
339  *
340  * Originally Released Under LGPL - original licence link has changed is not relivant.
341  *
342  * Fork - LGPL
343  * <script type="text/javascript">
344  */
345
346
347
348 /**
349  * @class Roo.data.Store
350  * @extends Roo.util.Observable
351  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
352  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
353  * <p>
354  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
355  * has no knowledge of the format of the data returned by the Proxy.<br>
356  * <p>
357  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
358  * instances from the data object. These records are cached and made available through accessor functions.
359  * @constructor
360  * Creates a new Store.
361  * @param {Object} config A config object containing the objects needed for the Store to access data,
362  * and read the data into Records.
363  */
364 Roo.data.Store = function(config){
365     this.data = new Roo.util.MixedCollection(false);
366     this.data.getKey = function(o){
367         return o.id;
368     };
369     this.baseParams = {};
370     // private
371     this.paramNames = {
372         "start" : "start",
373         "limit" : "limit",
374         "sort" : "sort",
375         "dir" : "dir",
376         "multisort" : "_multisort"
377     };
378
379     if(config && config.data){
380         this.inlineData = config.data;
381         delete config.data;
382     }
383
384     Roo.apply(this, config);
385     
386     if(this.reader){ // reader passed
387         this.reader = Roo.factory(this.reader, Roo.data);
388         this.reader.xmodule = this.xmodule || false;
389         if(!this.recordType){
390             this.recordType = this.reader.recordType;
391         }
392         if(this.reader.onMetaChange){
393             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
394         }
395     }
396
397     if(this.recordType){
398         this.fields = this.recordType.prototype.fields;
399     }
400     this.modified = [];
401
402     this.addEvents({
403         /**
404          * @event datachanged
405          * Fires when the data cache has changed, and a widget which is using this Store
406          * as a Record cache should refresh its view.
407          * @param {Store} this
408          */
409         datachanged : true,
410         /**
411          * @event metachange
412          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
413          * @param {Store} this
414          * @param {Object} meta The JSON metadata
415          */
416         metachange : true,
417         /**
418          * @event add
419          * Fires when Records have been added to the Store
420          * @param {Store} this
421          * @param {Roo.data.Record[]} records The array of Records added
422          * @param {Number} index The index at which the record(s) were added
423          */
424         add : true,
425         /**
426          * @event remove
427          * Fires when a Record has been removed from the Store
428          * @param {Store} this
429          * @param {Roo.data.Record} record The Record that was removed
430          * @param {Number} index The index at which the record was removed
431          */
432         remove : true,
433         /**
434          * @event update
435          * Fires when a Record has been updated
436          * @param {Store} this
437          * @param {Roo.data.Record} record The Record that was updated
438          * @param {String} operation The update operation being performed.  Value may be one of:
439          * <pre><code>
440  Roo.data.Record.EDIT
441  Roo.data.Record.REJECT
442  Roo.data.Record.COMMIT
443          * </code></pre>
444          */
445         update : true,
446         /**
447          * @event clear
448          * Fires when the data cache has been cleared.
449          * @param {Store} this
450          */
451         clear : true,
452         /**
453          * @event beforeload
454          * Fires before a request is made for a new data object.  If the beforeload handler returns false
455          * the load action will be canceled.
456          * @param {Store} this
457          * @param {Object} options The loading options that were specified (see {@link #load} for details)
458          */
459         beforeload : true,
460         /**
461          * @event beforeloadadd
462          * Fires after a new set of Records has been loaded.
463          * @param {Store} this
464          * @param {Roo.data.Record[]} records The Records that were loaded
465          * @param {Object} options The loading options that were specified (see {@link #load} for details)
466          */
467         beforeloadadd : true,
468         /**
469          * @event load
470          * Fires after a new set of Records has been loaded, before they are added to the store.
471          * @param {Store} this
472          * @param {Roo.data.Record[]} records The Records that were loaded
473          * @param {Object} options The loading options that were specified (see {@link #load} for details)
474          * @params {Object} return from reader
475          */
476         load : true,
477         /**
478          * @event loadexception
479          * Fires if an exception occurs in the Proxy during loading.
480          * Called with the signature of the Proxy's "loadexception" event.
481          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
482          * 
483          * @param {Proxy} 
484          * @param {Object} return from JsonData.reader() - success, totalRecords, records
485          * @param {Object} load options 
486          * @param {Object} jsonData from your request (normally this contains the Exception)
487          */
488         loadexception : true
489     });
490     
491     if(this.proxy){
492         this.proxy = Roo.factory(this.proxy, Roo.data);
493         this.proxy.xmodule = this.xmodule || false;
494         this.relayEvents(this.proxy,  ["loadexception"]);
495     }
496     this.sortToggle = {};
497     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
498
499     Roo.data.Store.superclass.constructor.call(this);
500
501     if(this.inlineData){
502         this.loadData(this.inlineData);
503         delete this.inlineData;
504     }
505 };
506
507 Roo.extend(Roo.data.Store, Roo.util.Observable, {
508      /**
509     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
510     * without a remote query - used by combo/forms at present.
511     */
512     
513     /**
514     * @cfg {Roo.data.DataProxy} proxy 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.originalValue = this.resetValue;
16535         this.setValue(this.resetValue);
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.clearInvalid();
19233         this.lastData = false;
19234         if (this.view) {
19235             this.view.clearSelections();
19236         }
19237     },
19238     // private
19239     findRecord : function(prop, value){
19240         var record;
19241         if(this.store.getCount() > 0){
19242             this.store.each(function(r){
19243                 if(r.data[prop] == value){
19244                     record = r;
19245                     return false;
19246                 }
19247                 return true;
19248             });
19249         }
19250         return record;
19251     },
19252     
19253     getName: function()
19254     {
19255         // returns hidden if it's set..
19256         if (!this.rendered) {return ''};
19257         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
19258         
19259     },
19260     // private
19261     onViewMove : function(e, t){
19262         this.inKeyMode = false;
19263     },
19264
19265     // private
19266     onViewOver : function(e, t){
19267         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19268             return;
19269         }
19270         var item = this.view.findItemFromChild(t);
19271         if(item){
19272             var index = this.view.indexOf(item);
19273             this.select(index, false);
19274         }
19275     },
19276
19277     // private
19278     onViewClick : function(doFocus)
19279     {
19280         var index = this.view.getSelectedIndexes()[0];
19281         var r = this.store.getAt(index);
19282         if(r){
19283             this.onSelect(r, index);
19284         }
19285         if(doFocus !== false && !this.blockFocus){
19286             this.el.focus();
19287         }
19288     },
19289
19290     // private
19291     restrictHeight : function(){
19292         this.innerList.dom.style.height = '';
19293         var inner = this.innerList.dom;
19294         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19295         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19296         this.list.beginUpdate();
19297         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19298         this.list.alignTo(this.el, this.listAlign);
19299         this.list.endUpdate();
19300     },
19301
19302     // private
19303     onEmptyResults : function(){
19304         this.collapse();
19305     },
19306
19307     /**
19308      * Returns true if the dropdown list is expanded, else false.
19309      */
19310     isExpanded : function(){
19311         return this.list.isVisible();
19312     },
19313
19314     /**
19315      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19316      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19317      * @param {String} value The data value of the item to select
19318      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19319      * selected item if it is not currently in view (defaults to true)
19320      * @return {Boolean} True if the value matched an item in the list, else false
19321      */
19322     selectByValue : function(v, scrollIntoView){
19323         if(v !== undefined && v !== null){
19324             var r = this.findRecord(this.valueField || this.displayField, v);
19325             if(r){
19326                 this.select(this.store.indexOf(r), scrollIntoView);
19327                 return true;
19328             }
19329         }
19330         return false;
19331     },
19332
19333     /**
19334      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19335      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19336      * @param {Number} index The zero-based index of the list item to select
19337      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19338      * selected item if it is not currently in view (defaults to true)
19339      */
19340     select : function(index, scrollIntoView){
19341         this.selectedIndex = index;
19342         this.view.select(index);
19343         if(scrollIntoView !== false){
19344             var el = this.view.getNode(index);
19345             if(el){
19346                 this.innerList.scrollChildIntoView(el, false);
19347             }
19348         }
19349     },
19350
19351     // private
19352     selectNext : function(){
19353         var ct = this.store.getCount();
19354         if(ct > 0){
19355             if(this.selectedIndex == -1){
19356                 this.select(0);
19357             }else if(this.selectedIndex < ct-1){
19358                 this.select(this.selectedIndex+1);
19359             }
19360         }
19361     },
19362
19363     // private
19364     selectPrev : function(){
19365         var ct = this.store.getCount();
19366         if(ct > 0){
19367             if(this.selectedIndex == -1){
19368                 this.select(0);
19369             }else if(this.selectedIndex != 0){
19370                 this.select(this.selectedIndex-1);
19371             }
19372         }
19373     },
19374
19375     // private
19376     onKeyUp : function(e){
19377         if(this.editable !== false && !e.isSpecialKey()){
19378             this.lastKey = e.getKey();
19379             this.dqTask.delay(this.queryDelay);
19380         }
19381     },
19382
19383     // private
19384     validateBlur : function(){
19385         return !this.list || !this.list.isVisible();   
19386     },
19387
19388     // private
19389     initQuery : function(){
19390         this.doQuery(this.getRawValue());
19391     },
19392
19393     // private
19394     doForce : function(){
19395         if(this.el.dom.value.length > 0){
19396             this.el.dom.value =
19397                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19398              
19399         }
19400     },
19401
19402     /**
19403      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19404      * query allowing the query action to be canceled if needed.
19405      * @param {String} query The SQL query to execute
19406      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19407      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19408      * saved in the current store (defaults to false)
19409      */
19410     doQuery : function(q, forceAll){
19411         if(q === undefined || q === null){
19412             q = '';
19413         }
19414         var qe = {
19415             query: q,
19416             forceAll: forceAll,
19417             combo: this,
19418             cancel:false
19419         };
19420         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19421             return false;
19422         }
19423         q = qe.query;
19424         forceAll = qe.forceAll;
19425         if(forceAll === true || (q.length >= this.minChars)){
19426             if(this.lastQuery != q || this.alwaysQuery){
19427                 this.lastQuery = q;
19428                 if(this.mode == 'local'){
19429                     this.selectedIndex = -1;
19430                     if(forceAll){
19431                         this.store.clearFilter();
19432                     }else{
19433                         this.store.filter(this.displayField, q);
19434                     }
19435                     this.onLoad();
19436                 }else{
19437                     this.store.baseParams[this.queryParam] = q;
19438                     this.store.load({
19439                         params: this.getParams(q)
19440                     });
19441                     this.expand();
19442                 }
19443             }else{
19444                 this.selectedIndex = -1;
19445                 this.onLoad();   
19446             }
19447         }
19448     },
19449
19450     // private
19451     getParams : function(q){
19452         var p = {};
19453         //p[this.queryParam] = q;
19454         if(this.pageSize){
19455             p.start = 0;
19456             p.limit = this.pageSize;
19457         }
19458         return p;
19459     },
19460
19461     /**
19462      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19463      */
19464     collapse : function(){
19465         if(!this.isExpanded()){
19466             return;
19467         }
19468         this.list.hide();
19469         Roo.get(document).un('mousedown', this.collapseIf, this);
19470         Roo.get(document).un('mousewheel', this.collapseIf, this);
19471         if (!this.editable) {
19472             Roo.get(document).un('keydown', this.listKeyPress, this);
19473         }
19474         this.fireEvent('collapse', this);
19475     },
19476
19477     // private
19478     collapseIf : function(e){
19479         if(!e.within(this.wrap) && !e.within(this.list)){
19480             this.collapse();
19481         }
19482     },
19483
19484     /**
19485      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19486      */
19487     expand : function(){
19488         if(this.isExpanded() || !this.hasFocus){
19489             return;
19490         }
19491         this.list.alignTo(this.el, this.listAlign);
19492         this.list.show();
19493         Roo.get(document).on('mousedown', this.collapseIf, this);
19494         Roo.get(document).on('mousewheel', this.collapseIf, this);
19495         if (!this.editable) {
19496             Roo.get(document).on('keydown', this.listKeyPress, this);
19497         }
19498         
19499         this.fireEvent('expand', this);
19500     },
19501
19502     // private
19503     // Implements the default empty TriggerField.onTriggerClick function
19504     onTriggerClick : function(){
19505         if(this.disabled){
19506             return;
19507         }
19508         if(this.isExpanded()){
19509             this.collapse();
19510             if (!this.blockFocus) {
19511                 this.el.focus();
19512             }
19513             
19514         }else {
19515             this.hasFocus = true;
19516             if(this.triggerAction == 'all') {
19517                 this.doQuery(this.allQuery, true);
19518             } else {
19519                 this.doQuery(this.getRawValue());
19520             }
19521             if (!this.blockFocus) {
19522                 this.el.focus();
19523             }
19524         }
19525     },
19526     listKeyPress : function(e)
19527     {
19528         //Roo.log('listkeypress');
19529         // scroll to first matching element based on key pres..
19530         if (e.isSpecialKey()) {
19531             return false;
19532         }
19533         var k = String.fromCharCode(e.getKey()).toUpperCase();
19534         //Roo.log(k);
19535         var match  = false;
19536         var csel = this.view.getSelectedNodes();
19537         var cselitem = false;
19538         if (csel.length) {
19539             var ix = this.view.indexOf(csel[0]);
19540             cselitem  = this.store.getAt(ix);
19541             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19542                 cselitem = false;
19543             }
19544             
19545         }
19546         
19547         this.store.each(function(v) { 
19548             if (cselitem) {
19549                 // start at existing selection.
19550                 if (cselitem.id == v.id) {
19551                     cselitem = false;
19552                 }
19553                 return;
19554             }
19555                 
19556             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19557                 match = this.store.indexOf(v);
19558                 return false;
19559             }
19560         }, this);
19561         
19562         if (match === false) {
19563             return true; // no more action?
19564         }
19565         // scroll to?
19566         this.view.select(match);
19567         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19568         sn.scrollIntoView(sn.dom.parentNode, false);
19569     }
19570
19571     /** 
19572     * @cfg {Boolean} grow 
19573     * @hide 
19574     */
19575     /** 
19576     * @cfg {Number} growMin 
19577     * @hide 
19578     */
19579     /** 
19580     * @cfg {Number} growMax 
19581     * @hide 
19582     */
19583     /**
19584      * @hide
19585      * @method autoSize
19586      */
19587 });/*
19588  * Copyright(c) 2010-2012, Roo J Solutions Limited
19589  *
19590  * Licence LGPL
19591  *
19592  */
19593
19594 /**
19595  * @class Roo.form.ComboBoxArray
19596  * @extends Roo.form.TextField
19597  * A facebook style adder... for lists of email / people / countries  etc...
19598  * pick multiple items from a combo box, and shows each one.
19599  *
19600  *  Fred [x]  Brian [x]  [Pick another |v]
19601  *
19602  *
19603  *  For this to work: it needs various extra information
19604  *    - normal combo problay has
19605  *      name, hiddenName
19606  *    + displayField, valueField
19607  *
19608  *    For our purpose...
19609  *
19610  *
19611  *   If we change from 'extends' to wrapping...
19612  *   
19613  *  
19614  *
19615  
19616  
19617  * @constructor
19618  * Create a new ComboBoxArray.
19619  * @param {Object} config Configuration options
19620  */
19621  
19622
19623 Roo.form.ComboBoxArray = function(config)
19624 {
19625     this.addEvents({
19626         /**
19627          * @event beforeremove
19628          * Fires before remove the value from the list
19629              * @param {Roo.form.ComboBoxArray} _self This combo box array
19630              * @param {Roo.form.ComboBoxArray.Item} item removed item
19631              */
19632         'beforeremove' : true,
19633         /**
19634          * @event remove
19635          * Fires when remove the value from the list
19636              * @param {Roo.form.ComboBoxArray} _self This combo box array
19637              * @param {Roo.form.ComboBoxArray.Item} item removed item
19638              */
19639         'remove' : true
19640         
19641         
19642     });
19643     
19644     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19645     
19646     this.items = new Roo.util.MixedCollection(false);
19647     
19648     // construct the child combo...
19649     
19650     
19651     
19652     
19653    
19654     
19655 }
19656
19657  
19658 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19659
19660     /**
19661      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
19662      */
19663     
19664     lastData : false,
19665     
19666     // behavies liek a hiddne field
19667     inputType:      'hidden',
19668     /**
19669      * @cfg {Number} width The width of the box that displays the selected element
19670      */ 
19671     width:          300,
19672
19673     
19674     
19675     /**
19676      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19677      */
19678     name : false,
19679     /**
19680      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19681      */
19682     hiddenName : false,
19683     
19684     
19685     // private the array of items that are displayed..
19686     items  : false,
19687     // private - the hidden field el.
19688     hiddenEl : false,
19689     // private - the filed el..
19690     el : false,
19691     
19692     //validateValue : function() { return true; }, // all values are ok!
19693     //onAddClick: function() { },
19694     
19695     onRender : function(ct, position) 
19696     {
19697         
19698         // create the standard hidden element
19699         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19700         
19701         
19702         // give fake names to child combo;
19703         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19704         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
19705         
19706         this.combo = Roo.factory(this.combo, Roo.form);
19707         this.combo.onRender(ct, position);
19708         if (typeof(this.combo.width) != 'undefined') {
19709             this.combo.onResize(this.combo.width,0);
19710         }
19711         
19712         this.combo.initEvents();
19713         
19714         // assigned so form know we need to do this..
19715         this.store          = this.combo.store;
19716         this.valueField     = this.combo.valueField;
19717         this.displayField   = this.combo.displayField ;
19718         
19719         
19720         this.combo.wrap.addClass('x-cbarray-grp');
19721         
19722         var cbwrap = this.combo.wrap.createChild(
19723             {tag: 'div', cls: 'x-cbarray-cb'},
19724             this.combo.el.dom
19725         );
19726         
19727              
19728         this.hiddenEl = this.combo.wrap.createChild({
19729             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19730         });
19731         this.el = this.combo.wrap.createChild({
19732             tag: 'input',  type:'hidden' , name: this.name, value : ''
19733         });
19734          //   this.el.dom.removeAttribute("name");
19735         
19736         
19737         this.outerWrap = this.combo.wrap;
19738         this.wrap = cbwrap;
19739         
19740         this.outerWrap.setWidth(this.width);
19741         this.outerWrap.dom.removeChild(this.el.dom);
19742         
19743         this.wrap.dom.appendChild(this.el.dom);
19744         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19745         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19746         
19747         this.combo.trigger.setStyle('position','relative');
19748         this.combo.trigger.setStyle('left', '0px');
19749         this.combo.trigger.setStyle('top', '2px');
19750         
19751         this.combo.el.setStyle('vertical-align', 'text-bottom');
19752         
19753         //this.trigger.setStyle('vertical-align', 'top');
19754         
19755         // this should use the code from combo really... on('add' ....)
19756         if (this.adder) {
19757             
19758         
19759             this.adder = this.outerWrap.createChild(
19760                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19761             var _t = this;
19762             this.adder.on('click', function(e) {
19763                 _t.fireEvent('adderclick', this, e);
19764             }, _t);
19765         }
19766         //var _t = this;
19767         //this.adder.on('click', this.onAddClick, _t);
19768         
19769         
19770         this.combo.on('select', function(cb, rec, ix) {
19771             this.addItem(rec.data);
19772             
19773             cb.setValue('');
19774             cb.el.dom.value = '';
19775             //cb.lastData = rec.data;
19776             // add to list
19777             
19778         }, this);
19779         
19780         
19781     },
19782     
19783     
19784     getName: function()
19785     {
19786         // returns hidden if it's set..
19787         if (!this.rendered) {return ''};
19788         return  this.hiddenName ? this.hiddenName : this.name;
19789         
19790     },
19791     
19792     
19793     onResize: function(w, h){
19794         
19795         return;
19796         // not sure if this is needed..
19797         //this.combo.onResize(w,h);
19798         
19799         if(typeof w != 'number'){
19800             // we do not handle it!?!?
19801             return;
19802         }
19803         var tw = this.combo.trigger.getWidth();
19804         tw += this.addicon ? this.addicon.getWidth() : 0;
19805         tw += this.editicon ? this.editicon.getWidth() : 0;
19806         var x = w - tw;
19807         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19808             
19809         this.combo.trigger.setStyle('left', '0px');
19810         
19811         if(this.list && this.listWidth === undefined){
19812             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19813             this.list.setWidth(lw);
19814             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19815         }
19816         
19817     
19818         
19819     },
19820     
19821     addItem: function(rec)
19822     {
19823         var valueField = this.combo.valueField;
19824         var displayField = this.combo.displayField;
19825         if (this.items.indexOfKey(rec[valueField]) > -1) {
19826             //console.log("GOT " + rec.data.id);
19827             return;
19828         }
19829         
19830         var x = new Roo.form.ComboBoxArray.Item({
19831             //id : rec[this.idField],
19832             data : rec,
19833             displayField : displayField ,
19834             tipField : displayField ,
19835             cb : this
19836         });
19837         // use the 
19838         this.items.add(rec[valueField],x);
19839         // add it before the element..
19840         this.updateHiddenEl();
19841         x.render(this.outerWrap, this.wrap.dom);
19842         // add the image handler..
19843     },
19844     
19845     updateHiddenEl : function()
19846     {
19847         this.validate();
19848         if (!this.hiddenEl) {
19849             return;
19850         }
19851         var ar = [];
19852         var idField = this.combo.valueField;
19853         
19854         this.items.each(function(f) {
19855             ar.push(f.data[idField]);
19856            
19857         });
19858         this.hiddenEl.dom.value = ar.join(',');
19859         this.validate();
19860     },
19861     
19862     reset : function()
19863     {
19864         this.items.clear();
19865         
19866         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19867            el.remove();
19868         });
19869         
19870         this.el.dom.value = '';
19871         if (this.hiddenEl) {
19872             this.hiddenEl.dom.value = '';
19873         }
19874         
19875     },
19876     getValue: function()
19877     {
19878         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19879     },
19880     setValue: function(v) // not a valid action - must use addItems..
19881     {
19882          
19883         this.reset();
19884         
19885         
19886         
19887         if (this.store.isLocal && (typeof(v) == 'string')) {
19888             // then we can use the store to find the values..
19889             // comma seperated at present.. this needs to allow JSON based encoding..
19890             this.hiddenEl.value  = v;
19891             var v_ar = [];
19892             Roo.each(v.split(','), function(k) {
19893                 Roo.log("CHECK " + this.valueField + ',' + k);
19894                 var li = this.store.query(this.valueField, k);
19895                 if (!li.length) {
19896                     return;
19897                 }
19898                 var add = {};
19899                 add[this.valueField] = k;
19900                 add[this.displayField] = li.item(0).data[this.displayField];
19901                 
19902                 this.addItem(add);
19903             }, this) 
19904              
19905         }
19906         if (typeof(v) == 'object' ) {
19907             // then let's assume it's an array of objects..
19908             Roo.each(v, function(l) {
19909                 this.addItem(l);
19910             }, this);
19911              
19912         }
19913         
19914         
19915     },
19916     setFromData: function(v)
19917     {
19918         // this recieves an object, if setValues is called.
19919         this.reset();
19920         this.el.dom.value = v[this.displayField];
19921         this.hiddenEl.dom.value = v[this.valueField];
19922         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19923             return;
19924         }
19925         var kv = v[this.valueField];
19926         var dv = v[this.displayField];
19927         kv = typeof(kv) != 'string' ? '' : kv;
19928         dv = typeof(dv) != 'string' ? '' : dv;
19929         
19930         
19931         var keys = kv.split(',');
19932         var display = dv.split(',');
19933         for (var i = 0 ; i < keys.length; i++) {
19934             
19935             add = {};
19936             add[this.valueField] = keys[i];
19937             add[this.displayField] = display[i];
19938             this.addItem(add);
19939         }
19940       
19941         
19942     },
19943     
19944     /**
19945      * Validates the combox array value
19946      * @return {Boolean} True if the value is valid, else false
19947      */
19948     validate : function(){
19949         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19950             this.clearInvalid();
19951             return true;
19952         }
19953         return false;
19954     },
19955     
19956     validateValue : function(value){
19957         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19958         
19959     },
19960     
19961     /*@
19962      * overide
19963      * 
19964      */
19965     isDirty : function() {
19966         if(this.disabled) {
19967             return false;
19968         }
19969         
19970         try {
19971             var d = Roo.decode(String(this.originalValue));
19972         } catch (e) {
19973             return String(this.getValue()) !== String(this.originalValue);
19974         }
19975         
19976         var originalValue = [];
19977         
19978         for (var i = 0; i < d.length; i++){
19979             originalValue.push(d[i][this.valueField]);
19980         }
19981         
19982         return String(this.getValue()) !== String(originalValue.join(','));
19983         
19984     }
19985     
19986 });
19987
19988
19989
19990 /**
19991  * @class Roo.form.ComboBoxArray.Item
19992  * @extends Roo.BoxComponent
19993  * A selected item in the list
19994  *  Fred [x]  Brian [x]  [Pick another |v]
19995  * 
19996  * @constructor
19997  * Create a new item.
19998  * @param {Object} config Configuration options
19999  */
20000  
20001 Roo.form.ComboBoxArray.Item = function(config) {
20002     config.id = Roo.id();
20003     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
20004 }
20005
20006 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
20007     data : {},
20008     cb: false,
20009     displayField : false,
20010     tipField : false,
20011     
20012     
20013     defaultAutoCreate : {
20014         tag: 'div',
20015         cls: 'x-cbarray-item',
20016         cn : [ 
20017             { tag: 'div' },
20018             {
20019                 tag: 'img',
20020                 width:16,
20021                 height : 16,
20022                 src : Roo.BLANK_IMAGE_URL ,
20023                 align: 'center'
20024             }
20025         ]
20026         
20027     },
20028     
20029  
20030     onRender : function(ct, position)
20031     {
20032         Roo.form.Field.superclass.onRender.call(this, ct, position);
20033         
20034         if(!this.el){
20035             var cfg = this.getAutoCreate();
20036             this.el = ct.createChild(cfg, position);
20037         }
20038         
20039         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
20040         
20041         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
20042             this.cb.renderer(this.data) :
20043             String.format('{0}',this.data[this.displayField]);
20044         
20045             
20046         this.el.child('div').dom.setAttribute('qtip',
20047                         String.format('{0}',this.data[this.tipField])
20048         );
20049         
20050         this.el.child('img').on('click', this.remove, this);
20051         
20052     },
20053    
20054     remove : function()
20055     {
20056         if(this.cb.disabled){
20057             return;
20058         }
20059         
20060         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
20061             this.cb.items.remove(this);
20062             this.el.child('img').un('click', this.remove, this);
20063             this.el.remove();
20064             this.cb.updateHiddenEl();
20065
20066             this.cb.fireEvent('remove', this.cb, this);
20067         }
20068         
20069     }
20070 });/*
20071  * Based on:
20072  * Ext JS Library 1.1.1
20073  * Copyright(c) 2006-2007, Ext JS, LLC.
20074  *
20075  * Originally Released Under LGPL - original licence link has changed is not relivant.
20076  *
20077  * Fork - LGPL
20078  * <script type="text/javascript">
20079  */
20080 /**
20081  * @class Roo.form.Checkbox
20082  * @extends Roo.form.Field
20083  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20084  * @constructor
20085  * Creates a new Checkbox
20086  * @param {Object} config Configuration options
20087  */
20088 Roo.form.Checkbox = function(config){
20089     Roo.form.Checkbox.superclass.constructor.call(this, config);
20090     this.addEvents({
20091         /**
20092          * @event check
20093          * Fires when the checkbox is checked or unchecked.
20094              * @param {Roo.form.Checkbox} this This checkbox
20095              * @param {Boolean} checked The new checked value
20096              */
20097         check : true
20098     });
20099 };
20100
20101 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20102     /**
20103      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20104      */
20105     focusClass : undefined,
20106     /**
20107      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20108      */
20109     fieldClass: "x-form-field",
20110     /**
20111      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20112      */
20113     checked: false,
20114     /**
20115      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20116      * {tag: "input", type: "checkbox", autocomplete: "off"})
20117      */
20118     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20119     /**
20120      * @cfg {String} boxLabel The text that appears beside the checkbox
20121      */
20122     boxLabel : "",
20123     /**
20124      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20125      */  
20126     inputValue : '1',
20127     /**
20128      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20129      */
20130      valueOff: '0', // value when not checked..
20131
20132     actionMode : 'viewEl', 
20133     //
20134     // private
20135     itemCls : 'x-menu-check-item x-form-item',
20136     groupClass : 'x-menu-group-item',
20137     inputType : 'hidden',
20138     
20139     
20140     inSetChecked: false, // check that we are not calling self...
20141     
20142     inputElement: false, // real input element?
20143     basedOn: false, // ????
20144     
20145     isFormField: true, // not sure where this is needed!!!!
20146
20147     onResize : function(){
20148         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20149         if(!this.boxLabel){
20150             this.el.alignTo(this.wrap, 'c-c');
20151         }
20152     },
20153
20154     initEvents : function(){
20155         Roo.form.Checkbox.superclass.initEvents.call(this);
20156         this.el.on("click", this.onClick,  this);
20157         this.el.on("change", this.onClick,  this);
20158     },
20159
20160
20161     getResizeEl : function(){
20162         return this.wrap;
20163     },
20164
20165     getPositionEl : function(){
20166         return this.wrap;
20167     },
20168
20169     // private
20170     onRender : function(ct, position){
20171         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20172         /*
20173         if(this.inputValue !== undefined){
20174             this.el.dom.value = this.inputValue;
20175         }
20176         */
20177         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20178         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20179         var viewEl = this.wrap.createChild({ 
20180             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20181         this.viewEl = viewEl;   
20182         this.wrap.on('click', this.onClick,  this); 
20183         
20184         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20185         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20186         
20187         
20188         
20189         if(this.boxLabel){
20190             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20191         //    viewEl.on('click', this.onClick,  this); 
20192         }
20193         //if(this.checked){
20194             this.setChecked(this.checked);
20195         //}else{
20196             //this.checked = this.el.dom;
20197         //}
20198
20199     },
20200
20201     // private
20202     initValue : Roo.emptyFn,
20203
20204     /**
20205      * Returns the checked state of the checkbox.
20206      * @return {Boolean} True if checked, else false
20207      */
20208     getValue : function(){
20209         if(this.el){
20210             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20211         }
20212         return this.valueOff;
20213         
20214     },
20215
20216         // private
20217     onClick : function(){ 
20218         if (this.disabled) {
20219             return;
20220         }
20221         this.setChecked(!this.checked);
20222
20223         //if(this.el.dom.checked != this.checked){
20224         //    this.setValue(this.el.dom.checked);
20225        // }
20226     },
20227
20228     /**
20229      * Sets the checked state of the checkbox.
20230      * On is always based on a string comparison between inputValue and the param.
20231      * @param {Boolean/String} value - the value to set 
20232      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20233      */
20234     setValue : function(v,suppressEvent){
20235         
20236         
20237         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20238         //if(this.el && this.el.dom){
20239         //    this.el.dom.checked = this.checked;
20240         //    this.el.dom.defaultChecked = this.checked;
20241         //}
20242         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20243         //this.fireEvent("check", this, this.checked);
20244     },
20245     // private..
20246     setChecked : function(state,suppressEvent)
20247     {
20248         if (this.inSetChecked) {
20249             this.checked = state;
20250             return;
20251         }
20252         
20253     
20254         if(this.wrap){
20255             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20256         }
20257         this.checked = state;
20258         if(suppressEvent !== true){
20259             this.fireEvent('check', this, state);
20260         }
20261         this.inSetChecked = true;
20262         this.el.dom.value = state ? this.inputValue : this.valueOff;
20263         this.inSetChecked = false;
20264         
20265     },
20266     // handle setting of hidden value by some other method!!?!?
20267     setFromHidden: function()
20268     {
20269         if(!this.el){
20270             return;
20271         }
20272         //console.log("SET FROM HIDDEN");
20273         //alert('setFrom hidden');
20274         this.setValue(this.el.dom.value);
20275     },
20276     
20277     onDestroy : function()
20278     {
20279         if(this.viewEl){
20280             Roo.get(this.viewEl).remove();
20281         }
20282          
20283         Roo.form.Checkbox.superclass.onDestroy.call(this);
20284     },
20285     
20286     setBoxLabel : function(str)
20287     {
20288         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20289     }
20290
20291 });/*
20292  * Based on:
20293  * Ext JS Library 1.1.1
20294  * Copyright(c) 2006-2007, Ext JS, LLC.
20295  *
20296  * Originally Released Under LGPL - original licence link has changed is not relivant.
20297  *
20298  * Fork - LGPL
20299  * <script type="text/javascript">
20300  */
20301  
20302 /**
20303  * @class Roo.form.Radio
20304  * @extends Roo.form.Checkbox
20305  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20306  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20307  * @constructor
20308  * Creates a new Radio
20309  * @param {Object} config Configuration options
20310  */
20311 Roo.form.Radio = function(){
20312     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20313 };
20314 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20315     inputType: 'radio',
20316
20317     /**
20318      * If this radio is part of a group, it will return the selected value
20319      * @return {String}
20320      */
20321     getGroupValue : function(){
20322         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20323     },
20324     
20325     
20326     onRender : function(ct, position){
20327         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20328         
20329         if(this.inputValue !== undefined){
20330             this.el.dom.value = this.inputValue;
20331         }
20332          
20333         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20334         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20335         //var viewEl = this.wrap.createChild({ 
20336         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20337         //this.viewEl = viewEl;   
20338         //this.wrap.on('click', this.onClick,  this); 
20339         
20340         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20341         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20342         
20343         
20344         
20345         if(this.boxLabel){
20346             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20347         //    viewEl.on('click', this.onClick,  this); 
20348         }
20349          if(this.checked){
20350             this.el.dom.checked =   'checked' ;
20351         }
20352          
20353     } 
20354     
20355     
20356 });//<script type="text/javascript">
20357
20358 /*
20359  * Based  Ext JS Library 1.1.1
20360  * Copyright(c) 2006-2007, Ext JS, LLC.
20361  * LGPL
20362  *
20363  */
20364  
20365 /**
20366  * @class Roo.HtmlEditorCore
20367  * @extends Roo.Component
20368  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20369  *
20370  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20371  */
20372
20373 Roo.HtmlEditorCore = function(config){
20374     
20375     
20376     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20377     
20378     
20379     this.addEvents({
20380         /**
20381          * @event initialize
20382          * Fires when the editor is fully initialized (including the iframe)
20383          * @param {Roo.HtmlEditorCore} this
20384          */
20385         initialize: true,
20386         /**
20387          * @event activate
20388          * Fires when the editor is first receives the focus. Any insertion must wait
20389          * until after this event.
20390          * @param {Roo.HtmlEditorCore} this
20391          */
20392         activate: true,
20393          /**
20394          * @event beforesync
20395          * Fires before the textarea is updated with content from the editor iframe. Return false
20396          * to cancel the sync.
20397          * @param {Roo.HtmlEditorCore} this
20398          * @param {String} html
20399          */
20400         beforesync: true,
20401          /**
20402          * @event beforepush
20403          * Fires before the iframe editor is updated with content from the textarea. Return false
20404          * to cancel the push.
20405          * @param {Roo.HtmlEditorCore} this
20406          * @param {String} html
20407          */
20408         beforepush: true,
20409          /**
20410          * @event sync
20411          * Fires when the textarea is updated with content from the editor iframe.
20412          * @param {Roo.HtmlEditorCore} this
20413          * @param {String} html
20414          */
20415         sync: true,
20416          /**
20417          * @event push
20418          * Fires when the iframe editor is updated with content from the textarea.
20419          * @param {Roo.HtmlEditorCore} this
20420          * @param {String} html
20421          */
20422         push: true,
20423         
20424         /**
20425          * @event editorevent
20426          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20427          * @param {Roo.HtmlEditorCore} this
20428          */
20429         editorevent: true
20430         
20431     });
20432     
20433     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20434     
20435     // defaults : white / black...
20436     this.applyBlacklists();
20437     
20438     
20439     
20440 };
20441
20442
20443 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20444
20445
20446      /**
20447      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20448      */
20449     
20450     owner : false,
20451     
20452      /**
20453      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20454      *                        Roo.resizable.
20455      */
20456     resizable : false,
20457      /**
20458      * @cfg {Number} height (in pixels)
20459      */   
20460     height: 300,
20461    /**
20462      * @cfg {Number} width (in pixels)
20463      */   
20464     width: 500,
20465     
20466     /**
20467      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20468      * 
20469      */
20470     stylesheets: false,
20471     
20472     // id of frame..
20473     frameId: false,
20474     
20475     // private properties
20476     validationEvent : false,
20477     deferHeight: true,
20478     initialized : false,
20479     activated : false,
20480     sourceEditMode : false,
20481     onFocus : Roo.emptyFn,
20482     iframePad:3,
20483     hideMode:'offsets',
20484     
20485     clearUp: true,
20486     
20487     // blacklist + whitelisted elements..
20488     black: false,
20489     white: false,
20490      
20491     bodyCls : '',
20492
20493     /**
20494      * Protected method that will not generally be called directly. It
20495      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20496      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20497      */
20498     getDocMarkup : function(){
20499         // body styles..
20500         var st = '';
20501         
20502         // inherit styels from page...?? 
20503         if (this.stylesheets === false) {
20504             
20505             Roo.get(document.head).select('style').each(function(node) {
20506                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20507             });
20508             
20509             Roo.get(document.head).select('link').each(function(node) { 
20510                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20511             });
20512             
20513         } else if (!this.stylesheets.length) {
20514                 // simple..
20515                 st = '<style type="text/css">' +
20516                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20517                    '</style>';
20518         } else { 
20519             st = '<style type="text/css">' +
20520                     this.stylesheets +
20521                 '</style>';
20522         }
20523         
20524         st +=  '<style type="text/css">' +
20525             'IMG { cursor: pointer } ' +
20526         '</style>';
20527
20528         var cls = 'roo-htmleditor-body';
20529         
20530         if(this.bodyCls.length){
20531             cls += ' ' + this.bodyCls;
20532         }
20533         
20534         return '<html><head>' + st  +
20535             //<style type="text/css">' +
20536             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20537             //'</style>' +
20538             ' </head><body class="' +  cls + '"></body></html>';
20539     },
20540
20541     // private
20542     onRender : function(ct, position)
20543     {
20544         var _t = this;
20545         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20546         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20547         
20548         
20549         this.el.dom.style.border = '0 none';
20550         this.el.dom.setAttribute('tabIndex', -1);
20551         this.el.addClass('x-hidden hide');
20552         
20553         
20554         
20555         if(Roo.isIE){ // fix IE 1px bogus margin
20556             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20557         }
20558        
20559         
20560         this.frameId = Roo.id();
20561         
20562          
20563         
20564         var iframe = this.owner.wrap.createChild({
20565             tag: 'iframe',
20566             cls: 'form-control', // bootstrap..
20567             id: this.frameId,
20568             name: this.frameId,
20569             frameBorder : 'no',
20570             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20571         }, this.el
20572         );
20573         
20574         
20575         this.iframe = iframe.dom;
20576
20577          this.assignDocWin();
20578         
20579         this.doc.designMode = 'on';
20580        
20581         this.doc.open();
20582         this.doc.write(this.getDocMarkup());
20583         this.doc.close();
20584
20585         
20586         var task = { // must defer to wait for browser to be ready
20587             run : function(){
20588                 //console.log("run task?" + this.doc.readyState);
20589                 this.assignDocWin();
20590                 if(this.doc.body || this.doc.readyState == 'complete'){
20591                     try {
20592                         this.doc.designMode="on";
20593                     } catch (e) {
20594                         return;
20595                     }
20596                     Roo.TaskMgr.stop(task);
20597                     this.initEditor.defer(10, this);
20598                 }
20599             },
20600             interval : 10,
20601             duration: 10000,
20602             scope: this
20603         };
20604         Roo.TaskMgr.start(task);
20605
20606     },
20607
20608     // private
20609     onResize : function(w, h)
20610     {
20611          Roo.log('resize: ' +w + ',' + h );
20612         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20613         if(!this.iframe){
20614             return;
20615         }
20616         if(typeof w == 'number'){
20617             
20618             this.iframe.style.width = w + 'px';
20619         }
20620         if(typeof h == 'number'){
20621             
20622             this.iframe.style.height = h + 'px';
20623             if(this.doc){
20624                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20625             }
20626         }
20627         
20628     },
20629
20630     /**
20631      * Toggles the editor between standard and source edit mode.
20632      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20633      */
20634     toggleSourceEdit : function(sourceEditMode){
20635         
20636         this.sourceEditMode = sourceEditMode === true;
20637         
20638         if(this.sourceEditMode){
20639  
20640             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20641             
20642         }else{
20643             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20644             //this.iframe.className = '';
20645             this.deferFocus();
20646         }
20647         //this.setSize(this.owner.wrap.getSize());
20648         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20649     },
20650
20651     
20652   
20653
20654     /**
20655      * Protected method that will not generally be called directly. If you need/want
20656      * custom HTML cleanup, this is the method you should override.
20657      * @param {String} html The HTML to be cleaned
20658      * return {String} The cleaned HTML
20659      */
20660     cleanHtml : function(html){
20661         html = String(html);
20662         if(html.length > 5){
20663             if(Roo.isSafari){ // strip safari nonsense
20664                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20665             }
20666         }
20667         if(html == '&nbsp;'){
20668             html = '';
20669         }
20670         return html;
20671     },
20672
20673     /**
20674      * HTML Editor -> Textarea
20675      * Protected method that will not generally be called directly. Syncs the contents
20676      * of the editor iframe with the textarea.
20677      */
20678     syncValue : function(){
20679         if(this.initialized){
20680             var bd = (this.doc.body || this.doc.documentElement);
20681             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20682             var html = bd.innerHTML;
20683             if(Roo.isSafari){
20684                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20685                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20686                 if(m && m[1]){
20687                     html = '<div style="'+m[0]+'">' + html + '</div>';
20688                 }
20689             }
20690             html = this.cleanHtml(html);
20691             // fix up the special chars.. normaly like back quotes in word...
20692             // however we do not want to do this with chinese..
20693             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20694                 var cc = b.charCodeAt();
20695                 if (
20696                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20697                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20698                     (cc >= 0xf900 && cc < 0xfb00 )
20699                 ) {
20700                         return b;
20701                 }
20702                 return "&#"+cc+";" 
20703             });
20704             if(this.owner.fireEvent('beforesync', this, html) !== false){
20705                 this.el.dom.value = html;
20706                 this.owner.fireEvent('sync', this, html);
20707             }
20708         }
20709     },
20710
20711     /**
20712      * Protected method that will not generally be called directly. Pushes the value of the textarea
20713      * into the iframe editor.
20714      */
20715     pushValue : function(){
20716         if(this.initialized){
20717             var v = this.el.dom.value.trim();
20718             
20719 //            if(v.length < 1){
20720 //                v = '&#160;';
20721 //            }
20722             
20723             if(this.owner.fireEvent('beforepush', this, v) !== false){
20724                 var d = (this.doc.body || this.doc.documentElement);
20725                 d.innerHTML = v;
20726                 this.cleanUpPaste();
20727                 this.el.dom.value = d.innerHTML;
20728                 this.owner.fireEvent('push', this, v);
20729             }
20730         }
20731     },
20732
20733     // private
20734     deferFocus : function(){
20735         this.focus.defer(10, this);
20736     },
20737
20738     // doc'ed in Field
20739     focus : function(){
20740         if(this.win && !this.sourceEditMode){
20741             this.win.focus();
20742         }else{
20743             this.el.focus();
20744         }
20745     },
20746     
20747     assignDocWin: function()
20748     {
20749         var iframe = this.iframe;
20750         
20751          if(Roo.isIE){
20752             this.doc = iframe.contentWindow.document;
20753             this.win = iframe.contentWindow;
20754         } else {
20755 //            if (!Roo.get(this.frameId)) {
20756 //                return;
20757 //            }
20758 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20759 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20760             
20761             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20762                 return;
20763             }
20764             
20765             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20766             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20767         }
20768     },
20769     
20770     // private
20771     initEditor : function(){
20772         //console.log("INIT EDITOR");
20773         this.assignDocWin();
20774         
20775         
20776         
20777         this.doc.designMode="on";
20778         this.doc.open();
20779         this.doc.write(this.getDocMarkup());
20780         this.doc.close();
20781         
20782         var dbody = (this.doc.body || this.doc.documentElement);
20783         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20784         // this copies styles from the containing element into thsi one..
20785         // not sure why we need all of this..
20786         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20787         
20788         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20789         //ss['background-attachment'] = 'fixed'; // w3c
20790         dbody.bgProperties = 'fixed'; // ie
20791         //Roo.DomHelper.applyStyles(dbody, ss);
20792         Roo.EventManager.on(this.doc, {
20793             //'mousedown': this.onEditorEvent,
20794             'mouseup': this.onEditorEvent,
20795             'dblclick': this.onEditorEvent,
20796             'click': this.onEditorEvent,
20797             'keyup': this.onEditorEvent,
20798             buffer:100,
20799             scope: this
20800         });
20801         if(Roo.isGecko){
20802             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20803         }
20804         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20805             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20806         }
20807         this.initialized = true;
20808
20809         this.owner.fireEvent('initialize', this);
20810         this.pushValue();
20811     },
20812
20813     // private
20814     onDestroy : function(){
20815         
20816         
20817         
20818         if(this.rendered){
20819             
20820             //for (var i =0; i < this.toolbars.length;i++) {
20821             //    // fixme - ask toolbars for heights?
20822             //    this.toolbars[i].onDestroy();
20823            // }
20824             
20825             //this.wrap.dom.innerHTML = '';
20826             //this.wrap.remove();
20827         }
20828     },
20829
20830     // private
20831     onFirstFocus : function(){
20832         
20833         this.assignDocWin();
20834         
20835         
20836         this.activated = true;
20837          
20838     
20839         if(Roo.isGecko){ // prevent silly gecko errors
20840             this.win.focus();
20841             var s = this.win.getSelection();
20842             if(!s.focusNode || s.focusNode.nodeType != 3){
20843                 var r = s.getRangeAt(0);
20844                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20845                 r.collapse(true);
20846                 this.deferFocus();
20847             }
20848             try{
20849                 this.execCmd('useCSS', true);
20850                 this.execCmd('styleWithCSS', false);
20851             }catch(e){}
20852         }
20853         this.owner.fireEvent('activate', this);
20854     },
20855
20856     // private
20857     adjustFont: function(btn){
20858         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20859         //if(Roo.isSafari){ // safari
20860         //    adjust *= 2;
20861        // }
20862         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20863         if(Roo.isSafari){ // safari
20864             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20865             v =  (v < 10) ? 10 : v;
20866             v =  (v > 48) ? 48 : v;
20867             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20868             
20869         }
20870         
20871         
20872         v = Math.max(1, v+adjust);
20873         
20874         this.execCmd('FontSize', v  );
20875     },
20876
20877     onEditorEvent : function(e)
20878     {
20879         this.owner.fireEvent('editorevent', this, e);
20880       //  this.updateToolbar();
20881         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20882     },
20883
20884     insertTag : function(tg)
20885     {
20886         // could be a bit smarter... -> wrap the current selected tRoo..
20887         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20888             
20889             range = this.createRange(this.getSelection());
20890             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20891             wrappingNode.appendChild(range.extractContents());
20892             range.insertNode(wrappingNode);
20893
20894             return;
20895             
20896             
20897             
20898         }
20899         this.execCmd("formatblock",   tg);
20900         
20901     },
20902     
20903     insertText : function(txt)
20904     {
20905         
20906         
20907         var range = this.createRange();
20908         range.deleteContents();
20909                //alert(Sender.getAttribute('label'));
20910                
20911         range.insertNode(this.doc.createTextNode(txt));
20912     } ,
20913     
20914      
20915
20916     /**
20917      * Executes a Midas editor command on the editor document and performs necessary focus and
20918      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20919      * @param {String} cmd The Midas command
20920      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20921      */
20922     relayCmd : function(cmd, value){
20923         this.win.focus();
20924         this.execCmd(cmd, value);
20925         this.owner.fireEvent('editorevent', this);
20926         //this.updateToolbar();
20927         this.owner.deferFocus();
20928     },
20929
20930     /**
20931      * Executes a Midas editor command directly on the editor document.
20932      * For visual commands, you should use {@link #relayCmd} instead.
20933      * <b>This should only be called after the editor is initialized.</b>
20934      * @param {String} cmd The Midas command
20935      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20936      */
20937     execCmd : function(cmd, value){
20938         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20939         this.syncValue();
20940     },
20941  
20942  
20943    
20944     /**
20945      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20946      * to insert tRoo.
20947      * @param {String} text | dom node.. 
20948      */
20949     insertAtCursor : function(text)
20950     {
20951         
20952         if(!this.activated){
20953             return;
20954         }
20955         /*
20956         if(Roo.isIE){
20957             this.win.focus();
20958             var r = this.doc.selection.createRange();
20959             if(r){
20960                 r.collapse(true);
20961                 r.pasteHTML(text);
20962                 this.syncValue();
20963                 this.deferFocus();
20964             
20965             }
20966             return;
20967         }
20968         */
20969         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20970             this.win.focus();
20971             
20972             
20973             // from jquery ui (MIT licenced)
20974             var range, node;
20975             var win = this.win;
20976             
20977             if (win.getSelection && win.getSelection().getRangeAt) {
20978                 range = win.getSelection().getRangeAt(0);
20979                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20980                 range.insertNode(node);
20981             } else if (win.document.selection && win.document.selection.createRange) {
20982                 // no firefox support
20983                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20984                 win.document.selection.createRange().pasteHTML(txt);
20985             } else {
20986                 // no firefox support
20987                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20988                 this.execCmd('InsertHTML', txt);
20989             } 
20990             
20991             this.syncValue();
20992             
20993             this.deferFocus();
20994         }
20995     },
20996  // private
20997     mozKeyPress : function(e){
20998         if(e.ctrlKey){
20999             var c = e.getCharCode(), cmd;
21000           
21001             if(c > 0){
21002                 c = String.fromCharCode(c).toLowerCase();
21003                 switch(c){
21004                     case 'b':
21005                         cmd = 'bold';
21006                         break;
21007                     case 'i':
21008                         cmd = 'italic';
21009                         break;
21010                     
21011                     case 'u':
21012                         cmd = 'underline';
21013                         break;
21014                     
21015                     case 'v':
21016                         this.cleanUpPaste.defer(100, this);
21017                         return;
21018                         
21019                 }
21020                 if(cmd){
21021                     this.win.focus();
21022                     this.execCmd(cmd);
21023                     this.deferFocus();
21024                     e.preventDefault();
21025                 }
21026                 
21027             }
21028         }
21029     },
21030
21031     // private
21032     fixKeys : function(){ // load time branching for fastest keydown performance
21033         if(Roo.isIE){
21034             return function(e){
21035                 var k = e.getKey(), r;
21036                 if(k == e.TAB){
21037                     e.stopEvent();
21038                     r = this.doc.selection.createRange();
21039                     if(r){
21040                         r.collapse(true);
21041                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21042                         this.deferFocus();
21043                     }
21044                     return;
21045                 }
21046                 
21047                 if(k == e.ENTER){
21048                     r = this.doc.selection.createRange();
21049                     if(r){
21050                         var target = r.parentElement();
21051                         if(!target || target.tagName.toLowerCase() != 'li'){
21052                             e.stopEvent();
21053                             r.pasteHTML('<br />');
21054                             r.collapse(false);
21055                             r.select();
21056                         }
21057                     }
21058                 }
21059                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21060                     this.cleanUpPaste.defer(100, this);
21061                     return;
21062                 }
21063                 
21064                 
21065             };
21066         }else if(Roo.isOpera){
21067             return function(e){
21068                 var k = e.getKey();
21069                 if(k == e.TAB){
21070                     e.stopEvent();
21071                     this.win.focus();
21072                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21073                     this.deferFocus();
21074                 }
21075                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21076                     this.cleanUpPaste.defer(100, this);
21077                     return;
21078                 }
21079                 
21080             };
21081         }else if(Roo.isSafari){
21082             return function(e){
21083                 var k = e.getKey();
21084                 
21085                 if(k == e.TAB){
21086                     e.stopEvent();
21087                     this.execCmd('InsertText','\t');
21088                     this.deferFocus();
21089                     return;
21090                 }
21091                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21092                     this.cleanUpPaste.defer(100, this);
21093                     return;
21094                 }
21095                 
21096              };
21097         }
21098     }(),
21099     
21100     getAllAncestors: function()
21101     {
21102         var p = this.getSelectedNode();
21103         var a = [];
21104         if (!p) {
21105             a.push(p); // push blank onto stack..
21106             p = this.getParentElement();
21107         }
21108         
21109         
21110         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21111             a.push(p);
21112             p = p.parentNode;
21113         }
21114         a.push(this.doc.body);
21115         return a;
21116     },
21117     lastSel : false,
21118     lastSelNode : false,
21119     
21120     
21121     getSelection : function() 
21122     {
21123         this.assignDocWin();
21124         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21125     },
21126     
21127     getSelectedNode: function() 
21128     {
21129         // this may only work on Gecko!!!
21130         
21131         // should we cache this!!!!
21132         
21133         
21134         
21135          
21136         var range = this.createRange(this.getSelection()).cloneRange();
21137         
21138         if (Roo.isIE) {
21139             var parent = range.parentElement();
21140             while (true) {
21141                 var testRange = range.duplicate();
21142                 testRange.moveToElementText(parent);
21143                 if (testRange.inRange(range)) {
21144                     break;
21145                 }
21146                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21147                     break;
21148                 }
21149                 parent = parent.parentElement;
21150             }
21151             return parent;
21152         }
21153         
21154         // is ancestor a text element.
21155         var ac =  range.commonAncestorContainer;
21156         if (ac.nodeType == 3) {
21157             ac = ac.parentNode;
21158         }
21159         
21160         var ar = ac.childNodes;
21161          
21162         var nodes = [];
21163         var other_nodes = [];
21164         var has_other_nodes = false;
21165         for (var i=0;i<ar.length;i++) {
21166             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21167                 continue;
21168             }
21169             // fullly contained node.
21170             
21171             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21172                 nodes.push(ar[i]);
21173                 continue;
21174             }
21175             
21176             // probably selected..
21177             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21178                 other_nodes.push(ar[i]);
21179                 continue;
21180             }
21181             // outer..
21182             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21183                 continue;
21184             }
21185             
21186             
21187             has_other_nodes = true;
21188         }
21189         if (!nodes.length && other_nodes.length) {
21190             nodes= other_nodes;
21191         }
21192         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21193             return false;
21194         }
21195         
21196         return nodes[0];
21197     },
21198     createRange: function(sel)
21199     {
21200         // this has strange effects when using with 
21201         // top toolbar - not sure if it's a great idea.
21202         //this.editor.contentWindow.focus();
21203         if (typeof sel != "undefined") {
21204             try {
21205                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21206             } catch(e) {
21207                 return this.doc.createRange();
21208             }
21209         } else {
21210             return this.doc.createRange();
21211         }
21212     },
21213     getParentElement: function()
21214     {
21215         
21216         this.assignDocWin();
21217         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21218         
21219         var range = this.createRange(sel);
21220          
21221         try {
21222             var p = range.commonAncestorContainer;
21223             while (p.nodeType == 3) { // text node
21224                 p = p.parentNode;
21225             }
21226             return p;
21227         } catch (e) {
21228             return null;
21229         }
21230     
21231     },
21232     /***
21233      *
21234      * Range intersection.. the hard stuff...
21235      *  '-1' = before
21236      *  '0' = hits..
21237      *  '1' = after.
21238      *         [ -- selected range --- ]
21239      *   [fail]                        [fail]
21240      *
21241      *    basically..
21242      *      if end is before start or  hits it. fail.
21243      *      if start is after end or hits it fail.
21244      *
21245      *   if either hits (but other is outside. - then it's not 
21246      *   
21247      *    
21248      **/
21249     
21250     
21251     // @see http://www.thismuchiknow.co.uk/?p=64.
21252     rangeIntersectsNode : function(range, node)
21253     {
21254         var nodeRange = node.ownerDocument.createRange();
21255         try {
21256             nodeRange.selectNode(node);
21257         } catch (e) {
21258             nodeRange.selectNodeContents(node);
21259         }
21260     
21261         var rangeStartRange = range.cloneRange();
21262         rangeStartRange.collapse(true);
21263     
21264         var rangeEndRange = range.cloneRange();
21265         rangeEndRange.collapse(false);
21266     
21267         var nodeStartRange = nodeRange.cloneRange();
21268         nodeStartRange.collapse(true);
21269     
21270         var nodeEndRange = nodeRange.cloneRange();
21271         nodeEndRange.collapse(false);
21272     
21273         return rangeStartRange.compareBoundaryPoints(
21274                  Range.START_TO_START, nodeEndRange) == -1 &&
21275                rangeEndRange.compareBoundaryPoints(
21276                  Range.START_TO_START, nodeStartRange) == 1;
21277         
21278          
21279     },
21280     rangeCompareNode : function(range, node)
21281     {
21282         var nodeRange = node.ownerDocument.createRange();
21283         try {
21284             nodeRange.selectNode(node);
21285         } catch (e) {
21286             nodeRange.selectNodeContents(node);
21287         }
21288         
21289         
21290         range.collapse(true);
21291     
21292         nodeRange.collapse(true);
21293      
21294         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21295         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21296          
21297         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21298         
21299         var nodeIsBefore   =  ss == 1;
21300         var nodeIsAfter    = ee == -1;
21301         
21302         if (nodeIsBefore && nodeIsAfter) {
21303             return 0; // outer
21304         }
21305         if (!nodeIsBefore && nodeIsAfter) {
21306             return 1; //right trailed.
21307         }
21308         
21309         if (nodeIsBefore && !nodeIsAfter) {
21310             return 2;  // left trailed.
21311         }
21312         // fully contined.
21313         return 3;
21314     },
21315
21316     // private? - in a new class?
21317     cleanUpPaste :  function()
21318     {
21319         // cleans up the whole document..
21320         Roo.log('cleanuppaste');
21321         
21322         this.cleanUpChildren(this.doc.body);
21323         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21324         if (clean != this.doc.body.innerHTML) {
21325             this.doc.body.innerHTML = clean;
21326         }
21327         
21328     },
21329     
21330     cleanWordChars : function(input) {// change the chars to hex code
21331         var he = Roo.HtmlEditorCore;
21332         
21333         var output = input;
21334         Roo.each(he.swapCodes, function(sw) { 
21335             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21336             
21337             output = output.replace(swapper, sw[1]);
21338         });
21339         
21340         return output;
21341     },
21342     
21343     
21344     cleanUpChildren : function (n)
21345     {
21346         if (!n.childNodes.length) {
21347             return;
21348         }
21349         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21350            this.cleanUpChild(n.childNodes[i]);
21351         }
21352     },
21353     
21354     
21355         
21356     
21357     cleanUpChild : function (node)
21358     {
21359         var ed = this;
21360         //console.log(node);
21361         if (node.nodeName == "#text") {
21362             // clean up silly Windows -- stuff?
21363             return; 
21364         }
21365         if (node.nodeName == "#comment") {
21366             node.parentNode.removeChild(node);
21367             // clean up silly Windows -- stuff?
21368             return; 
21369         }
21370         var lcname = node.tagName.toLowerCase();
21371         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21372         // whitelist of tags..
21373         
21374         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21375             // remove node.
21376             node.parentNode.removeChild(node);
21377             return;
21378             
21379         }
21380         
21381         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21382         
21383         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21384         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21385         
21386         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21387         //    remove_keep_children = true;
21388         //}
21389         
21390         if (remove_keep_children) {
21391             this.cleanUpChildren(node);
21392             // inserts everything just before this node...
21393             while (node.childNodes.length) {
21394                 var cn = node.childNodes[0];
21395                 node.removeChild(cn);
21396                 node.parentNode.insertBefore(cn, node);
21397             }
21398             node.parentNode.removeChild(node);
21399             return;
21400         }
21401         
21402         if (!node.attributes || !node.attributes.length) {
21403             this.cleanUpChildren(node);
21404             return;
21405         }
21406         
21407         function cleanAttr(n,v)
21408         {
21409             
21410             if (v.match(/^\./) || v.match(/^\//)) {
21411                 return;
21412             }
21413             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21414                 return;
21415             }
21416             if (v.match(/^#/)) {
21417                 return;
21418             }
21419 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21420             node.removeAttribute(n);
21421             
21422         }
21423         
21424         var cwhite = this.cwhite;
21425         var cblack = this.cblack;
21426             
21427         function cleanStyle(n,v)
21428         {
21429             if (v.match(/expression/)) { //XSS?? should we even bother..
21430                 node.removeAttribute(n);
21431                 return;
21432             }
21433             
21434             var parts = v.split(/;/);
21435             var clean = [];
21436             
21437             Roo.each(parts, function(p) {
21438                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21439                 if (!p.length) {
21440                     return true;
21441                 }
21442                 var l = p.split(':').shift().replace(/\s+/g,'');
21443                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21444                 
21445                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21446 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21447                     //node.removeAttribute(n);
21448                     return true;
21449                 }
21450                 //Roo.log()
21451                 // only allow 'c whitelisted system attributes'
21452                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21453 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21454                     //node.removeAttribute(n);
21455                     return true;
21456                 }
21457                 
21458                 
21459                  
21460                 
21461                 clean.push(p);
21462                 return true;
21463             });
21464             if (clean.length) { 
21465                 node.setAttribute(n, clean.join(';'));
21466             } else {
21467                 node.removeAttribute(n);
21468             }
21469             
21470         }
21471         
21472         
21473         for (var i = node.attributes.length-1; i > -1 ; i--) {
21474             var a = node.attributes[i];
21475             //console.log(a);
21476             
21477             if (a.name.toLowerCase().substr(0,2)=='on')  {
21478                 node.removeAttribute(a.name);
21479                 continue;
21480             }
21481             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21482                 node.removeAttribute(a.name);
21483                 continue;
21484             }
21485             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21486                 cleanAttr(a.name,a.value); // fixme..
21487                 continue;
21488             }
21489             if (a.name == 'style') {
21490                 cleanStyle(a.name,a.value);
21491                 continue;
21492             }
21493             /// clean up MS crap..
21494             // tecnically this should be a list of valid class'es..
21495             
21496             
21497             if (a.name == 'class') {
21498                 if (a.value.match(/^Mso/)) {
21499                     node.className = '';
21500                 }
21501                 
21502                 if (a.value.match(/^body$/)) {
21503                     node.className = '';
21504                 }
21505                 continue;
21506             }
21507             
21508             // style cleanup!?
21509             // class cleanup?
21510             
21511         }
21512         
21513         
21514         this.cleanUpChildren(node);
21515         
21516         
21517     },
21518     
21519     /**
21520      * Clean up MS wordisms...
21521      */
21522     cleanWord : function(node)
21523     {
21524         
21525         
21526         if (!node) {
21527             this.cleanWord(this.doc.body);
21528             return;
21529         }
21530         if (node.nodeName == "#text") {
21531             // clean up silly Windows -- stuff?
21532             return; 
21533         }
21534         if (node.nodeName == "#comment") {
21535             node.parentNode.removeChild(node);
21536             // clean up silly Windows -- stuff?
21537             return; 
21538         }
21539         
21540         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21541             node.parentNode.removeChild(node);
21542             return;
21543         }
21544         
21545         // remove - but keep children..
21546         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21547             while (node.childNodes.length) {
21548                 var cn = node.childNodes[0];
21549                 node.removeChild(cn);
21550                 node.parentNode.insertBefore(cn, node);
21551             }
21552             node.parentNode.removeChild(node);
21553             this.iterateChildren(node, this.cleanWord);
21554             return;
21555         }
21556         // clean styles
21557         if (node.className.length) {
21558             
21559             var cn = node.className.split(/\W+/);
21560             var cna = [];
21561             Roo.each(cn, function(cls) {
21562                 if (cls.match(/Mso[a-zA-Z]+/)) {
21563                     return;
21564                 }
21565                 cna.push(cls);
21566             });
21567             node.className = cna.length ? cna.join(' ') : '';
21568             if (!cna.length) {
21569                 node.removeAttribute("class");
21570             }
21571         }
21572         
21573         if (node.hasAttribute("lang")) {
21574             node.removeAttribute("lang");
21575         }
21576         
21577         if (node.hasAttribute("style")) {
21578             
21579             var styles = node.getAttribute("style").split(";");
21580             var nstyle = [];
21581             Roo.each(styles, function(s) {
21582                 if (!s.match(/:/)) {
21583                     return;
21584                 }
21585                 var kv = s.split(":");
21586                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21587                     return;
21588                 }
21589                 // what ever is left... we allow.
21590                 nstyle.push(s);
21591             });
21592             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21593             if (!nstyle.length) {
21594                 node.removeAttribute('style');
21595             }
21596         }
21597         this.iterateChildren(node, this.cleanWord);
21598         
21599         
21600         
21601     },
21602     /**
21603      * iterateChildren of a Node, calling fn each time, using this as the scole..
21604      * @param {DomNode} node node to iterate children of.
21605      * @param {Function} fn method of this class to call on each item.
21606      */
21607     iterateChildren : function(node, fn)
21608     {
21609         if (!node.childNodes.length) {
21610                 return;
21611         }
21612         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21613            fn.call(this, node.childNodes[i])
21614         }
21615     },
21616     
21617     
21618     /**
21619      * cleanTableWidths.
21620      *
21621      * Quite often pasting from word etc.. results in tables with column and widths.
21622      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21623      *
21624      */
21625     cleanTableWidths : function(node)
21626     {
21627          
21628          
21629         if (!node) {
21630             this.cleanTableWidths(this.doc.body);
21631             return;
21632         }
21633         
21634         // ignore list...
21635         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21636             return; 
21637         }
21638         Roo.log(node.tagName);
21639         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21640             this.iterateChildren(node, this.cleanTableWidths);
21641             return;
21642         }
21643         if (node.hasAttribute('width')) {
21644             node.removeAttribute('width');
21645         }
21646         
21647          
21648         if (node.hasAttribute("style")) {
21649             // pretty basic...
21650             
21651             var styles = node.getAttribute("style").split(";");
21652             var nstyle = [];
21653             Roo.each(styles, function(s) {
21654                 if (!s.match(/:/)) {
21655                     return;
21656                 }
21657                 var kv = s.split(":");
21658                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21659                     return;
21660                 }
21661                 // what ever is left... we allow.
21662                 nstyle.push(s);
21663             });
21664             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21665             if (!nstyle.length) {
21666                 node.removeAttribute('style');
21667             }
21668         }
21669         
21670         this.iterateChildren(node, this.cleanTableWidths);
21671         
21672         
21673     },
21674     
21675     
21676     
21677     
21678     domToHTML : function(currentElement, depth, nopadtext) {
21679         
21680         depth = depth || 0;
21681         nopadtext = nopadtext || false;
21682     
21683         if (!currentElement) {
21684             return this.domToHTML(this.doc.body);
21685         }
21686         
21687         //Roo.log(currentElement);
21688         var j;
21689         var allText = false;
21690         var nodeName = currentElement.nodeName;
21691         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21692         
21693         if  (nodeName == '#text') {
21694             
21695             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21696         }
21697         
21698         
21699         var ret = '';
21700         if (nodeName != 'BODY') {
21701              
21702             var i = 0;
21703             // Prints the node tagName, such as <A>, <IMG>, etc
21704             if (tagName) {
21705                 var attr = [];
21706                 for(i = 0; i < currentElement.attributes.length;i++) {
21707                     // quoting?
21708                     var aname = currentElement.attributes.item(i).name;
21709                     if (!currentElement.attributes.item(i).value.length) {
21710                         continue;
21711                     }
21712                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21713                 }
21714                 
21715                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21716             } 
21717             else {
21718                 
21719                 // eack
21720             }
21721         } else {
21722             tagName = false;
21723         }
21724         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21725             return ret;
21726         }
21727         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21728             nopadtext = true;
21729         }
21730         
21731         
21732         // Traverse the tree
21733         i = 0;
21734         var currentElementChild = currentElement.childNodes.item(i);
21735         var allText = true;
21736         var innerHTML  = '';
21737         lastnode = '';
21738         while (currentElementChild) {
21739             // Formatting code (indent the tree so it looks nice on the screen)
21740             var nopad = nopadtext;
21741             if (lastnode == 'SPAN') {
21742                 nopad  = true;
21743             }
21744             // text
21745             if  (currentElementChild.nodeName == '#text') {
21746                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21747                 toadd = nopadtext ? toadd : toadd.trim();
21748                 if (!nopad && toadd.length > 80) {
21749                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21750                 }
21751                 innerHTML  += toadd;
21752                 
21753                 i++;
21754                 currentElementChild = currentElement.childNodes.item(i);
21755                 lastNode = '';
21756                 continue;
21757             }
21758             allText = false;
21759             
21760             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21761                 
21762             // Recursively traverse the tree structure of the child node
21763             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21764             lastnode = currentElementChild.nodeName;
21765             i++;
21766             currentElementChild=currentElement.childNodes.item(i);
21767         }
21768         
21769         ret += innerHTML;
21770         
21771         if (!allText) {
21772                 // The remaining code is mostly for formatting the tree
21773             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21774         }
21775         
21776         
21777         if (tagName) {
21778             ret+= "</"+tagName+">";
21779         }
21780         return ret;
21781         
21782     },
21783         
21784     applyBlacklists : function()
21785     {
21786         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21787         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21788         
21789         this.white = [];
21790         this.black = [];
21791         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21792             if (b.indexOf(tag) > -1) {
21793                 return;
21794             }
21795             this.white.push(tag);
21796             
21797         }, this);
21798         
21799         Roo.each(w, function(tag) {
21800             if (b.indexOf(tag) > -1) {
21801                 return;
21802             }
21803             if (this.white.indexOf(tag) > -1) {
21804                 return;
21805             }
21806             this.white.push(tag);
21807             
21808         }, this);
21809         
21810         
21811         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21812             if (w.indexOf(tag) > -1) {
21813                 return;
21814             }
21815             this.black.push(tag);
21816             
21817         }, this);
21818         
21819         Roo.each(b, function(tag) {
21820             if (w.indexOf(tag) > -1) {
21821                 return;
21822             }
21823             if (this.black.indexOf(tag) > -1) {
21824                 return;
21825             }
21826             this.black.push(tag);
21827             
21828         }, this);
21829         
21830         
21831         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21832         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21833         
21834         this.cwhite = [];
21835         this.cblack = [];
21836         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21837             if (b.indexOf(tag) > -1) {
21838                 return;
21839             }
21840             this.cwhite.push(tag);
21841             
21842         }, this);
21843         
21844         Roo.each(w, function(tag) {
21845             if (b.indexOf(tag) > -1) {
21846                 return;
21847             }
21848             if (this.cwhite.indexOf(tag) > -1) {
21849                 return;
21850             }
21851             this.cwhite.push(tag);
21852             
21853         }, this);
21854         
21855         
21856         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21857             if (w.indexOf(tag) > -1) {
21858                 return;
21859             }
21860             this.cblack.push(tag);
21861             
21862         }, this);
21863         
21864         Roo.each(b, function(tag) {
21865             if (w.indexOf(tag) > -1) {
21866                 return;
21867             }
21868             if (this.cblack.indexOf(tag) > -1) {
21869                 return;
21870             }
21871             this.cblack.push(tag);
21872             
21873         }, this);
21874     },
21875     
21876     setStylesheets : function(stylesheets)
21877     {
21878         if(typeof(stylesheets) == 'string'){
21879             Roo.get(this.iframe.contentDocument.head).createChild({
21880                 tag : 'link',
21881                 rel : 'stylesheet',
21882                 type : 'text/css',
21883                 href : stylesheets
21884             });
21885             
21886             return;
21887         }
21888         var _this = this;
21889      
21890         Roo.each(stylesheets, function(s) {
21891             if(!s.length){
21892                 return;
21893             }
21894             
21895             Roo.get(_this.iframe.contentDocument.head).createChild({
21896                 tag : 'link',
21897                 rel : 'stylesheet',
21898                 type : 'text/css',
21899                 href : s
21900             });
21901         });
21902
21903         
21904     },
21905     
21906     removeStylesheets : function()
21907     {
21908         var _this = this;
21909         
21910         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21911             s.remove();
21912         });
21913     },
21914     
21915     setStyle : function(style)
21916     {
21917         Roo.get(this.iframe.contentDocument.head).createChild({
21918             tag : 'style',
21919             type : 'text/css',
21920             html : style
21921         });
21922
21923         return;
21924     }
21925     
21926     // hide stuff that is not compatible
21927     /**
21928      * @event blur
21929      * @hide
21930      */
21931     /**
21932      * @event change
21933      * @hide
21934      */
21935     /**
21936      * @event focus
21937      * @hide
21938      */
21939     /**
21940      * @event specialkey
21941      * @hide
21942      */
21943     /**
21944      * @cfg {String} fieldClass @hide
21945      */
21946     /**
21947      * @cfg {String} focusClass @hide
21948      */
21949     /**
21950      * @cfg {String} autoCreate @hide
21951      */
21952     /**
21953      * @cfg {String} inputType @hide
21954      */
21955     /**
21956      * @cfg {String} invalidClass @hide
21957      */
21958     /**
21959      * @cfg {String} invalidText @hide
21960      */
21961     /**
21962      * @cfg {String} msgFx @hide
21963      */
21964     /**
21965      * @cfg {String} validateOnBlur @hide
21966      */
21967 });
21968
21969 Roo.HtmlEditorCore.white = [
21970         'area', 'br', 'img', 'input', 'hr', 'wbr',
21971         
21972        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21973        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21974        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21975        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21976        'table',   'ul',         'xmp', 
21977        
21978        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21979       'thead',   'tr', 
21980      
21981       'dir', 'menu', 'ol', 'ul', 'dl',
21982        
21983       'embed',  'object'
21984 ];
21985
21986
21987 Roo.HtmlEditorCore.black = [
21988     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21989         'applet', // 
21990         'base',   'basefont', 'bgsound', 'blink',  'body', 
21991         'frame',  'frameset', 'head',    'html',   'ilayer', 
21992         'iframe', 'layer',  'link',     'meta',    'object',   
21993         'script', 'style' ,'title',  'xml' // clean later..
21994 ];
21995 Roo.HtmlEditorCore.clean = [
21996     'script', 'style', 'title', 'xml'
21997 ];
21998 Roo.HtmlEditorCore.remove = [
21999     'font'
22000 ];
22001 // attributes..
22002
22003 Roo.HtmlEditorCore.ablack = [
22004     'on'
22005 ];
22006     
22007 Roo.HtmlEditorCore.aclean = [ 
22008     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22009 ];
22010
22011 // protocols..
22012 Roo.HtmlEditorCore.pwhite= [
22013         'http',  'https',  'mailto'
22014 ];
22015
22016 // white listed style attributes.
22017 Roo.HtmlEditorCore.cwhite= [
22018       //  'text-align', /// default is to allow most things..
22019       
22020          
22021 //        'font-size'//??
22022 ];
22023
22024 // black listed style attributes.
22025 Roo.HtmlEditorCore.cblack= [
22026       //  'font-size' -- this can be set by the project 
22027 ];
22028
22029
22030 Roo.HtmlEditorCore.swapCodes   =[ 
22031     [    8211, "--" ], 
22032     [    8212, "--" ], 
22033     [    8216,  "'" ],  
22034     [    8217, "'" ],  
22035     [    8220, '"' ],  
22036     [    8221, '"' ],  
22037     [    8226, "*" ],  
22038     [    8230, "..." ]
22039 ]; 
22040
22041     //<script type="text/javascript">
22042
22043 /*
22044  * Ext JS Library 1.1.1
22045  * Copyright(c) 2006-2007, Ext JS, LLC.
22046  * Licence LGPL
22047  * 
22048  */
22049  
22050  
22051 Roo.form.HtmlEditor = function(config){
22052     
22053     
22054     
22055     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22056     
22057     if (!this.toolbars) {
22058         this.toolbars = [];
22059     }
22060     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22061     
22062     
22063 };
22064
22065 /**
22066  * @class Roo.form.HtmlEditor
22067  * @extends Roo.form.Field
22068  * Provides a lightweight HTML Editor component.
22069  *
22070  * This has been tested on Fireforx / Chrome.. IE may not be so great..
22071  * 
22072  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22073  * supported by this editor.</b><br/><br/>
22074  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22075  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22076  */
22077 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22078     /**
22079      * @cfg {Boolean} clearUp
22080      */
22081     clearUp : true,
22082       /**
22083      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22084      */
22085     toolbars : false,
22086    
22087      /**
22088      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22089      *                        Roo.resizable.
22090      */
22091     resizable : false,
22092      /**
22093      * @cfg {Number} height (in pixels)
22094      */   
22095     height: 300,
22096    /**
22097      * @cfg {Number} width (in pixels)
22098      */   
22099     width: 500,
22100     
22101     /**
22102      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22103      * 
22104      */
22105     stylesheets: false,
22106     
22107     
22108      /**
22109      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22110      * 
22111      */
22112     cblack: false,
22113     /**
22114      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22115      * 
22116      */
22117     cwhite: false,
22118     
22119      /**
22120      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22121      * 
22122      */
22123     black: false,
22124     /**
22125      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22126      * 
22127      */
22128     white: false,
22129     
22130     // id of frame..
22131     frameId: false,
22132     
22133     // private properties
22134     validationEvent : false,
22135     deferHeight: true,
22136     initialized : false,
22137     activated : false,
22138     
22139     onFocus : Roo.emptyFn,
22140     iframePad:3,
22141     hideMode:'offsets',
22142     
22143     actionMode : 'container', // defaults to hiding it...
22144     
22145     defaultAutoCreate : { // modified by initCompnoent..
22146         tag: "textarea",
22147         style:"width:500px;height:300px;",
22148         autocomplete: "new-password"
22149     },
22150
22151     // private
22152     initComponent : function(){
22153         this.addEvents({
22154             /**
22155              * @event initialize
22156              * Fires when the editor is fully initialized (including the iframe)
22157              * @param {HtmlEditor} this
22158              */
22159             initialize: true,
22160             /**
22161              * @event activate
22162              * Fires when the editor is first receives the focus. Any insertion must wait
22163              * until after this event.
22164              * @param {HtmlEditor} this
22165              */
22166             activate: true,
22167              /**
22168              * @event beforesync
22169              * Fires before the textarea is updated with content from the editor iframe. Return false
22170              * to cancel the sync.
22171              * @param {HtmlEditor} this
22172              * @param {String} html
22173              */
22174             beforesync: true,
22175              /**
22176              * @event beforepush
22177              * Fires before the iframe editor is updated with content from the textarea. Return false
22178              * to cancel the push.
22179              * @param {HtmlEditor} this
22180              * @param {String} html
22181              */
22182             beforepush: true,
22183              /**
22184              * @event sync
22185              * Fires when the textarea is updated with content from the editor iframe.
22186              * @param {HtmlEditor} this
22187              * @param {String} html
22188              */
22189             sync: true,
22190              /**
22191              * @event push
22192              * Fires when the iframe editor is updated with content from the textarea.
22193              * @param {HtmlEditor} this
22194              * @param {String} html
22195              */
22196             push: true,
22197              /**
22198              * @event editmodechange
22199              * Fires when the editor switches edit modes
22200              * @param {HtmlEditor} this
22201              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22202              */
22203             editmodechange: true,
22204             /**
22205              * @event editorevent
22206              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22207              * @param {HtmlEditor} this
22208              */
22209             editorevent: true,
22210             /**
22211              * @event firstfocus
22212              * Fires when on first focus - needed by toolbars..
22213              * @param {HtmlEditor} this
22214              */
22215             firstfocus: true,
22216             /**
22217              * @event autosave
22218              * Auto save the htmlEditor value as a file into Events
22219              * @param {HtmlEditor} this
22220              */
22221             autosave: true,
22222             /**
22223              * @event savedpreview
22224              * preview the saved version of htmlEditor
22225              * @param {HtmlEditor} this
22226              */
22227             savedpreview: true,
22228             
22229             /**
22230             * @event stylesheetsclick
22231             * Fires when press the Sytlesheets button
22232             * @param {Roo.HtmlEditorCore} this
22233             */
22234             stylesheetsclick: true
22235         });
22236         this.defaultAutoCreate =  {
22237             tag: "textarea",
22238             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22239             autocomplete: "new-password"
22240         };
22241     },
22242
22243     /**
22244      * Protected method that will not generally be called directly. It
22245      * is called when the editor creates its toolbar. Override this method if you need to
22246      * add custom toolbar buttons.
22247      * @param {HtmlEditor} editor
22248      */
22249     createToolbar : function(editor){
22250         Roo.log("create toolbars");
22251         if (!editor.toolbars || !editor.toolbars.length) {
22252             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22253         }
22254         
22255         for (var i =0 ; i < editor.toolbars.length;i++) {
22256             editor.toolbars[i] = Roo.factory(
22257                     typeof(editor.toolbars[i]) == 'string' ?
22258                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
22259                 Roo.form.HtmlEditor);
22260             editor.toolbars[i].init(editor);
22261         }
22262          
22263         
22264     },
22265
22266      
22267     // private
22268     onRender : function(ct, position)
22269     {
22270         var _t = this;
22271         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22272         
22273         this.wrap = this.el.wrap({
22274             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22275         });
22276         
22277         this.editorcore.onRender(ct, position);
22278          
22279         if (this.resizable) {
22280             this.resizeEl = new Roo.Resizable(this.wrap, {
22281                 pinned : true,
22282                 wrap: true,
22283                 dynamic : true,
22284                 minHeight : this.height,
22285                 height: this.height,
22286                 handles : this.resizable,
22287                 width: this.width,
22288                 listeners : {
22289                     resize : function(r, w, h) {
22290                         _t.onResize(w,h); // -something
22291                     }
22292                 }
22293             });
22294             
22295         }
22296         this.createToolbar(this);
22297        
22298         
22299         if(!this.width){
22300             this.setSize(this.wrap.getSize());
22301         }
22302         if (this.resizeEl) {
22303             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22304             // should trigger onReize..
22305         }
22306         
22307         this.keyNav = new Roo.KeyNav(this.el, {
22308             
22309             "tab" : function(e){
22310                 e.preventDefault();
22311                 
22312                 var value = this.getValue();
22313                 
22314                 var start = this.el.dom.selectionStart;
22315                 var end = this.el.dom.selectionEnd;
22316                 
22317                 if(!e.shiftKey){
22318                     
22319                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22320                     this.el.dom.setSelectionRange(end + 1, end + 1);
22321                     return;
22322                 }
22323                 
22324                 var f = value.substring(0, start).split("\t");
22325                 
22326                 if(f.pop().length != 0){
22327                     return;
22328                 }
22329                 
22330                 this.setValue(f.join("\t") + value.substring(end));
22331                 this.el.dom.setSelectionRange(start - 1, start - 1);
22332                 
22333             },
22334             
22335             "home" : function(e){
22336                 e.preventDefault();
22337                 
22338                 var curr = this.el.dom.selectionStart;
22339                 var lines = this.getValue().split("\n");
22340                 
22341                 if(!lines.length){
22342                     return;
22343                 }
22344                 
22345                 if(e.ctrlKey){
22346                     this.el.dom.setSelectionRange(0, 0);
22347                     return;
22348                 }
22349                 
22350                 var pos = 0;
22351                 
22352                 for (var i = 0; i < lines.length;i++) {
22353                     pos += lines[i].length;
22354                     
22355                     if(i != 0){
22356                         pos += 1;
22357                     }
22358                     
22359                     if(pos < curr){
22360                         continue;
22361                     }
22362                     
22363                     pos -= lines[i].length;
22364                     
22365                     break;
22366                 }
22367                 
22368                 if(!e.shiftKey){
22369                     this.el.dom.setSelectionRange(pos, pos);
22370                     return;
22371                 }
22372                 
22373                 this.el.dom.selectionStart = pos;
22374                 this.el.dom.selectionEnd = curr;
22375             },
22376             
22377             "end" : function(e){
22378                 e.preventDefault();
22379                 
22380                 var curr = this.el.dom.selectionStart;
22381                 var lines = this.getValue().split("\n");
22382                 
22383                 if(!lines.length){
22384                     return;
22385                 }
22386                 
22387                 if(e.ctrlKey){
22388                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22389                     return;
22390                 }
22391                 
22392                 var pos = 0;
22393                 
22394                 for (var i = 0; i < lines.length;i++) {
22395                     
22396                     pos += lines[i].length;
22397                     
22398                     if(i != 0){
22399                         pos += 1;
22400                     }
22401                     
22402                     if(pos < curr){
22403                         continue;
22404                     }
22405                     
22406                     break;
22407                 }
22408                 
22409                 if(!e.shiftKey){
22410                     this.el.dom.setSelectionRange(pos, pos);
22411                     return;
22412                 }
22413                 
22414                 this.el.dom.selectionStart = curr;
22415                 this.el.dom.selectionEnd = pos;
22416             },
22417
22418             scope : this,
22419
22420             doRelay : function(foo, bar, hname){
22421                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22422             },
22423
22424             forceKeyDown: true
22425         });
22426         
22427 //        if(this.autosave && this.w){
22428 //            this.autoSaveFn = setInterval(this.autosave, 1000);
22429 //        }
22430     },
22431
22432     // private
22433     onResize : function(w, h)
22434     {
22435         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22436         var ew = false;
22437         var eh = false;
22438         
22439         if(this.el ){
22440             if(typeof w == 'number'){
22441                 var aw = w - this.wrap.getFrameWidth('lr');
22442                 this.el.setWidth(this.adjustWidth('textarea', aw));
22443                 ew = aw;
22444             }
22445             if(typeof h == 'number'){
22446                 var tbh = 0;
22447                 for (var i =0; i < this.toolbars.length;i++) {
22448                     // fixme - ask toolbars for heights?
22449                     tbh += this.toolbars[i].tb.el.getHeight();
22450                     if (this.toolbars[i].footer) {
22451                         tbh += this.toolbars[i].footer.el.getHeight();
22452                     }
22453                 }
22454                 
22455                 
22456                 
22457                 
22458                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22459                 ah -= 5; // knock a few pixes off for look..
22460 //                Roo.log(ah);
22461                 this.el.setHeight(this.adjustWidth('textarea', ah));
22462                 var eh = ah;
22463             }
22464         }
22465         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22466         this.editorcore.onResize(ew,eh);
22467         
22468     },
22469
22470     /**
22471      * Toggles the editor between standard and source edit mode.
22472      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22473      */
22474     toggleSourceEdit : function(sourceEditMode)
22475     {
22476         this.editorcore.toggleSourceEdit(sourceEditMode);
22477         
22478         if(this.editorcore.sourceEditMode){
22479             Roo.log('editor - showing textarea');
22480             
22481 //            Roo.log('in');
22482 //            Roo.log(this.syncValue());
22483             this.editorcore.syncValue();
22484             this.el.removeClass('x-hidden');
22485             this.el.dom.removeAttribute('tabIndex');
22486             this.el.focus();
22487             
22488             for (var i = 0; i < this.toolbars.length; i++) {
22489                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22490                     this.toolbars[i].tb.hide();
22491                     this.toolbars[i].footer.hide();
22492                 }
22493             }
22494             
22495         }else{
22496             Roo.log('editor - hiding textarea');
22497 //            Roo.log('out')
22498 //            Roo.log(this.pushValue()); 
22499             this.editorcore.pushValue();
22500             
22501             this.el.addClass('x-hidden');
22502             this.el.dom.setAttribute('tabIndex', -1);
22503             
22504             for (var i = 0; i < this.toolbars.length; i++) {
22505                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22506                     this.toolbars[i].tb.show();
22507                     this.toolbars[i].footer.show();
22508                 }
22509             }
22510             
22511             //this.deferFocus();
22512         }
22513         
22514         this.setSize(this.wrap.getSize());
22515         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
22516         
22517         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22518     },
22519  
22520     // private (for BoxComponent)
22521     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22522
22523     // private (for BoxComponent)
22524     getResizeEl : function(){
22525         return this.wrap;
22526     },
22527
22528     // private (for BoxComponent)
22529     getPositionEl : function(){
22530         return this.wrap;
22531     },
22532
22533     // private
22534     initEvents : function(){
22535         this.originalValue = this.getValue();
22536     },
22537
22538     /**
22539      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22540      * @method
22541      */
22542     markInvalid : Roo.emptyFn,
22543     /**
22544      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22545      * @method
22546      */
22547     clearInvalid : Roo.emptyFn,
22548
22549     setValue : function(v){
22550         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
22551         this.editorcore.pushValue();
22552     },
22553
22554      
22555     // private
22556     deferFocus : function(){
22557         this.focus.defer(10, this);
22558     },
22559
22560     // doc'ed in Field
22561     focus : function(){
22562         this.editorcore.focus();
22563         
22564     },
22565       
22566
22567     // private
22568     onDestroy : function(){
22569         
22570         
22571         
22572         if(this.rendered){
22573             
22574             for (var i =0; i < this.toolbars.length;i++) {
22575                 // fixme - ask toolbars for heights?
22576                 this.toolbars[i].onDestroy();
22577             }
22578             
22579             this.wrap.dom.innerHTML = '';
22580             this.wrap.remove();
22581         }
22582     },
22583
22584     // private
22585     onFirstFocus : function(){
22586         //Roo.log("onFirstFocus");
22587         this.editorcore.onFirstFocus();
22588          for (var i =0; i < this.toolbars.length;i++) {
22589             this.toolbars[i].onFirstFocus();
22590         }
22591         
22592     },
22593     
22594     // private
22595     syncValue : function()
22596     {
22597         this.editorcore.syncValue();
22598     },
22599     
22600     pushValue : function()
22601     {
22602         this.editorcore.pushValue();
22603     },
22604     
22605     setStylesheets : function(stylesheets)
22606     {
22607         this.editorcore.setStylesheets(stylesheets);
22608     },
22609     
22610     removeStylesheets : function()
22611     {
22612         this.editorcore.removeStylesheets();
22613     }
22614      
22615     
22616     // hide stuff that is not compatible
22617     /**
22618      * @event blur
22619      * @hide
22620      */
22621     /**
22622      * @event change
22623      * @hide
22624      */
22625     /**
22626      * @event focus
22627      * @hide
22628      */
22629     /**
22630      * @event specialkey
22631      * @hide
22632      */
22633     /**
22634      * @cfg {String} fieldClass @hide
22635      */
22636     /**
22637      * @cfg {String} focusClass @hide
22638      */
22639     /**
22640      * @cfg {String} autoCreate @hide
22641      */
22642     /**
22643      * @cfg {String} inputType @hide
22644      */
22645     /**
22646      * @cfg {String} invalidClass @hide
22647      */
22648     /**
22649      * @cfg {String} invalidText @hide
22650      */
22651     /**
22652      * @cfg {String} msgFx @hide
22653      */
22654     /**
22655      * @cfg {String} validateOnBlur @hide
22656      */
22657 });
22658  
22659     // <script type="text/javascript">
22660 /*
22661  * Based on
22662  * Ext JS Library 1.1.1
22663  * Copyright(c) 2006-2007, Ext JS, LLC.
22664  *  
22665  
22666  */
22667
22668 /**
22669  * @class Roo.form.HtmlEditorToolbar1
22670  * Basic Toolbar
22671  * 
22672  * Usage:
22673  *
22674  new Roo.form.HtmlEditor({
22675     ....
22676     toolbars : [
22677         new Roo.form.HtmlEditorToolbar1({
22678             disable : { fonts: 1 , format: 1, ..., ... , ...],
22679             btns : [ .... ]
22680         })
22681     }
22682      
22683  * 
22684  * @cfg {Object} disable List of elements to disable..
22685  * @cfg {Array} btns List of additional buttons.
22686  * 
22687  * 
22688  * NEEDS Extra CSS? 
22689  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22690  */
22691  
22692 Roo.form.HtmlEditor.ToolbarStandard = function(config)
22693 {
22694     
22695     Roo.apply(this, config);
22696     
22697     // default disabled, based on 'good practice'..
22698     this.disable = this.disable || {};
22699     Roo.applyIf(this.disable, {
22700         fontSize : true,
22701         colors : true,
22702         specialElements : true
22703     });
22704     
22705     
22706     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22707     // dont call parent... till later.
22708 }
22709
22710 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
22711     
22712     tb: false,
22713     
22714     rendered: false,
22715     
22716     editor : false,
22717     editorcore : false,
22718     /**
22719      * @cfg {Object} disable  List of toolbar elements to disable
22720          
22721      */
22722     disable : false,
22723     
22724     
22725      /**
22726      * @cfg {String} createLinkText The default text for the create link prompt
22727      */
22728     createLinkText : 'Please enter the URL for the link:',
22729     /**
22730      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
22731      */
22732     defaultLinkValue : 'http:/'+'/',
22733    
22734     
22735       /**
22736      * @cfg {Array} fontFamilies An array of available font families
22737      */
22738     fontFamilies : [
22739         'Arial',
22740         'Courier New',
22741         'Tahoma',
22742         'Times New Roman',
22743         'Verdana'
22744     ],
22745     
22746     specialChars : [
22747            "&#169;",
22748           "&#174;",     
22749           "&#8482;",    
22750           "&#163;" ,    
22751          // "&#8212;",    
22752           "&#8230;",    
22753           "&#247;" ,    
22754         //  "&#225;" ,     ?? a acute?
22755            "&#8364;"    , //Euro
22756        //   "&#8220;"    ,
22757         //  "&#8221;"    ,
22758         //  "&#8226;"    ,
22759           "&#176;"  //   , // degrees
22760
22761          // "&#233;"     , // e ecute
22762          // "&#250;"     , // u ecute?
22763     ],
22764     
22765     specialElements : [
22766         {
22767             text: "Insert Table",
22768             xtype: 'MenuItem',
22769             xns : Roo.Menu,
22770             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
22771                 
22772         },
22773         {    
22774             text: "Insert Image",
22775             xtype: 'MenuItem',
22776             xns : Roo.Menu,
22777             ihtml : '<img src="about:blank"/>'
22778             
22779         }
22780         
22781          
22782     ],
22783     
22784     
22785     inputElements : [ 
22786             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
22787             "input:submit", "input:button", "select", "textarea", "label" ],
22788     formats : [
22789         ["p"] ,  
22790         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
22791         ["pre"],[ "code"], 
22792         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
22793         ['div'],['span']
22794     ],
22795     
22796     cleanStyles : [
22797         "font-size"
22798     ],
22799      /**
22800      * @cfg {String} defaultFont default font to use.
22801      */
22802     defaultFont: 'tahoma',
22803    
22804     fontSelect : false,
22805     
22806     
22807     formatCombo : false,
22808     
22809     init : function(editor)
22810     {
22811         this.editor = editor;
22812         this.editorcore = editor.editorcore ? editor.editorcore : editor;
22813         var editorcore = this.editorcore;
22814         
22815         var _t = this;
22816         
22817         var fid = editorcore.frameId;
22818         var etb = this;
22819         function btn(id, toggle, handler){
22820             var xid = fid + '-'+ id ;
22821             return {
22822                 id : xid,
22823                 cmd : id,
22824                 cls : 'x-btn-icon x-edit-'+id,
22825                 enableToggle:toggle !== false,
22826                 scope: _t, // was editor...
22827                 handler:handler||_t.relayBtnCmd,
22828                 clickEvent:'mousedown',
22829                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
22830                 tabIndex:-1
22831             };
22832         }
22833         
22834         
22835         
22836         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
22837         this.tb = tb;
22838          // stop form submits
22839         tb.el.on('click', function(e){
22840             e.preventDefault(); // what does this do?
22841         });
22842
22843         if(!this.disable.font) { // && !Roo.isSafari){
22844             /* why no safari for fonts 
22845             editor.fontSelect = tb.el.createChild({
22846                 tag:'select',
22847                 tabIndex: -1,
22848                 cls:'x-font-select',
22849                 html: this.createFontOptions()
22850             });
22851             
22852             editor.fontSelect.on('change', function(){
22853                 var font = editor.fontSelect.dom.value;
22854                 editor.relayCmd('fontname', font);
22855                 editor.deferFocus();
22856             }, editor);
22857             
22858             tb.add(
22859                 editor.fontSelect.dom,
22860                 '-'
22861             );
22862             */
22863             
22864         };
22865         if(!this.disable.formats){
22866             this.formatCombo = new Roo.form.ComboBox({
22867                 store: new Roo.data.SimpleStore({
22868                     id : 'tag',
22869                     fields: ['tag'],
22870                     data : this.formats // from states.js
22871                 }),
22872                 blockFocus : true,
22873                 name : '',
22874                 //autoCreate : {tag: "div",  size: "20"},
22875                 displayField:'tag',
22876                 typeAhead: false,
22877                 mode: 'local',
22878                 editable : false,
22879                 triggerAction: 'all',
22880                 emptyText:'Add tag',
22881                 selectOnFocus:true,
22882                 width:135,
22883                 listeners : {
22884                     'select': function(c, r, i) {
22885                         editorcore.insertTag(r.get('tag'));
22886                         editor.focus();
22887                     }
22888                 }
22889
22890             });
22891             tb.addField(this.formatCombo);
22892             
22893         }
22894         
22895         if(!this.disable.format){
22896             tb.add(
22897                 btn('bold'),
22898                 btn('italic'),
22899                 btn('underline'),
22900                 btn('strikethrough')
22901             );
22902         };
22903         if(!this.disable.fontSize){
22904             tb.add(
22905                 '-',
22906                 
22907                 
22908                 btn('increasefontsize', false, editorcore.adjustFont),
22909                 btn('decreasefontsize', false, editorcore.adjustFont)
22910             );
22911         };
22912         
22913         
22914         if(!this.disable.colors){
22915             tb.add(
22916                 '-', {
22917                     id:editorcore.frameId +'-forecolor',
22918                     cls:'x-btn-icon x-edit-forecolor',
22919                     clickEvent:'mousedown',
22920                     tooltip: this.buttonTips['forecolor'] || undefined,
22921                     tabIndex:-1,
22922                     menu : new Roo.menu.ColorMenu({
22923                         allowReselect: true,
22924                         focus: Roo.emptyFn,
22925                         value:'000000',
22926                         plain:true,
22927                         selectHandler: function(cp, color){
22928                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
22929                             editor.deferFocus();
22930                         },
22931                         scope: editorcore,
22932                         clickEvent:'mousedown'
22933                     })
22934                 }, {
22935                     id:editorcore.frameId +'backcolor',
22936                     cls:'x-btn-icon x-edit-backcolor',
22937                     clickEvent:'mousedown',
22938                     tooltip: this.buttonTips['backcolor'] || undefined,
22939                     tabIndex:-1,
22940                     menu : new Roo.menu.ColorMenu({
22941                         focus: Roo.emptyFn,
22942                         value:'FFFFFF',
22943                         plain:true,
22944                         allowReselect: true,
22945                         selectHandler: function(cp, color){
22946                             if(Roo.isGecko){
22947                                 editorcore.execCmd('useCSS', false);
22948                                 editorcore.execCmd('hilitecolor', color);
22949                                 editorcore.execCmd('useCSS', true);
22950                                 editor.deferFocus();
22951                             }else{
22952                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
22953                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
22954                                 editor.deferFocus();
22955                             }
22956                         },
22957                         scope:editorcore,
22958                         clickEvent:'mousedown'
22959                     })
22960                 }
22961             );
22962         };
22963         // now add all the items...
22964         
22965
22966         if(!this.disable.alignments){
22967             tb.add(
22968                 '-',
22969                 btn('justifyleft'),
22970                 btn('justifycenter'),
22971                 btn('justifyright')
22972             );
22973         };
22974
22975         //if(!Roo.isSafari){
22976             if(!this.disable.links){
22977                 tb.add(
22978                     '-',
22979                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
22980                 );
22981             };
22982
22983             if(!this.disable.lists){
22984                 tb.add(
22985                     '-',
22986                     btn('insertorderedlist'),
22987                     btn('insertunorderedlist')
22988                 );
22989             }
22990             if(!this.disable.sourceEdit){
22991                 tb.add(
22992                     '-',
22993                     btn('sourceedit', true, function(btn){
22994                         this.toggleSourceEdit(btn.pressed);
22995                     })
22996                 );
22997             }
22998         //}
22999         
23000         var smenu = { };
23001         // special menu.. - needs to be tidied up..
23002         if (!this.disable.special) {
23003             smenu = {
23004                 text: "&#169;",
23005                 cls: 'x-edit-none',
23006                 
23007                 menu : {
23008                     items : []
23009                 }
23010             };
23011             for (var i =0; i < this.specialChars.length; i++) {
23012                 smenu.menu.items.push({
23013                     
23014                     html: this.specialChars[i],
23015                     handler: function(a,b) {
23016                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23017                         //editor.insertAtCursor(a.html);
23018                         
23019                     },
23020                     tabIndex:-1
23021                 });
23022             }
23023             
23024             
23025             tb.add(smenu);
23026             
23027             
23028         }
23029         
23030         var cmenu = { };
23031         if (!this.disable.cleanStyles) {
23032             cmenu = {
23033                 cls: 'x-btn-icon x-btn-clear',
23034                 
23035                 menu : {
23036                     items : []
23037                 }
23038             };
23039             for (var i =0; i < this.cleanStyles.length; i++) {
23040                 cmenu.menu.items.push({
23041                     actiontype : this.cleanStyles[i],
23042                     html: 'Remove ' + this.cleanStyles[i],
23043                     handler: function(a,b) {
23044 //                        Roo.log(a);
23045 //                        Roo.log(b);
23046                         var c = Roo.get(editorcore.doc.body);
23047                         c.select('[style]').each(function(s) {
23048                             s.dom.style.removeProperty(a.actiontype);
23049                         });
23050                         editorcore.syncValue();
23051                     },
23052                     tabIndex:-1
23053                 });
23054             }
23055              cmenu.menu.items.push({
23056                 actiontype : 'tablewidths',
23057                 html: 'Remove Table Widths',
23058                 handler: function(a,b) {
23059                     editorcore.cleanTableWidths();
23060                     editorcore.syncValue();
23061                 },
23062                 tabIndex:-1
23063             });
23064             cmenu.menu.items.push({
23065                 actiontype : 'word',
23066                 html: 'Remove MS Word Formating',
23067                 handler: function(a,b) {
23068                     editorcore.cleanWord();
23069                     editorcore.syncValue();
23070                 },
23071                 tabIndex:-1
23072             });
23073             
23074             cmenu.menu.items.push({
23075                 actiontype : 'all',
23076                 html: 'Remove All Styles',
23077                 handler: function(a,b) {
23078                     
23079                     var c = Roo.get(editorcore.doc.body);
23080                     c.select('[style]').each(function(s) {
23081                         s.dom.removeAttribute('style');
23082                     });
23083                     editorcore.syncValue();
23084                 },
23085                 tabIndex:-1
23086             });
23087             
23088             cmenu.menu.items.push({
23089                 actiontype : 'all',
23090                 html: 'Remove All CSS Classes',
23091                 handler: function(a,b) {
23092                     
23093                     var c = Roo.get(editorcore.doc.body);
23094                     c.select('[class]').each(function(s) {
23095                         s.dom.className = '';
23096                     });
23097                     editorcore.syncValue();
23098                 },
23099                 tabIndex:-1
23100             });
23101             
23102              cmenu.menu.items.push({
23103                 actiontype : 'tidy',
23104                 html: 'Tidy HTML Source',
23105                 handler: function(a,b) {
23106                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
23107                     editorcore.syncValue();
23108                 },
23109                 tabIndex:-1
23110             });
23111             
23112             
23113             tb.add(cmenu);
23114         }
23115          
23116         if (!this.disable.specialElements) {
23117             var semenu = {
23118                 text: "Other;",
23119                 cls: 'x-edit-none',
23120                 menu : {
23121                     items : []
23122                 }
23123             };
23124             for (var i =0; i < this.specialElements.length; i++) {
23125                 semenu.menu.items.push(
23126                     Roo.apply({ 
23127                         handler: function(a,b) {
23128                             editor.insertAtCursor(this.ihtml);
23129                         }
23130                     }, this.specialElements[i])
23131                 );
23132                     
23133             }
23134             
23135             tb.add(semenu);
23136             
23137             
23138         }
23139          
23140         
23141         if (this.btns) {
23142             for(var i =0; i< this.btns.length;i++) {
23143                 var b = Roo.factory(this.btns[i],Roo.form);
23144                 b.cls =  'x-edit-none';
23145                 
23146                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23147                     b.cls += ' x-init-enable';
23148                 }
23149                 
23150                 b.scope = editorcore;
23151                 tb.add(b);
23152             }
23153         
23154         }
23155         
23156         
23157         
23158         // disable everything...
23159         
23160         this.tb.items.each(function(item){
23161             
23162            if(
23163                 item.id != editorcore.frameId+ '-sourceedit' && 
23164                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23165             ){
23166                 
23167                 item.disable();
23168             }
23169         });
23170         this.rendered = true;
23171         
23172         // the all the btns;
23173         editor.on('editorevent', this.updateToolbar, this);
23174         // other toolbars need to implement this..
23175         //editor.on('editmodechange', this.updateToolbar, this);
23176     },
23177     
23178     
23179     relayBtnCmd : function(btn) {
23180         this.editorcore.relayCmd(btn.cmd);
23181     },
23182     // private used internally
23183     createLink : function(){
23184         Roo.log("create link?");
23185         var url = prompt(this.createLinkText, this.defaultLinkValue);
23186         if(url && url != 'http:/'+'/'){
23187             this.editorcore.relayCmd('createlink', url);
23188         }
23189     },
23190
23191     
23192     /**
23193      * Protected method that will not generally be called directly. It triggers
23194      * a toolbar update by reading the markup state of the current selection in the editor.
23195      */
23196     updateToolbar: function(){
23197
23198         if(!this.editorcore.activated){
23199             this.editor.onFirstFocus();
23200             return;
23201         }
23202
23203         var btns = this.tb.items.map, 
23204             doc = this.editorcore.doc,
23205             frameId = this.editorcore.frameId;
23206
23207         if(!this.disable.font && !Roo.isSafari){
23208             /*
23209             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23210             if(name != this.fontSelect.dom.value){
23211                 this.fontSelect.dom.value = name;
23212             }
23213             */
23214         }
23215         if(!this.disable.format){
23216             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23217             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23218             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23219             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23220         }
23221         if(!this.disable.alignments){
23222             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23223             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23224             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23225         }
23226         if(!Roo.isSafari && !this.disable.lists){
23227             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23228             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23229         }
23230         
23231         var ans = this.editorcore.getAllAncestors();
23232         if (this.formatCombo) {
23233             
23234             
23235             var store = this.formatCombo.store;
23236             this.formatCombo.setValue("");
23237             for (var i =0; i < ans.length;i++) {
23238                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23239                     // select it..
23240                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23241                     break;
23242                 }
23243             }
23244         }
23245         
23246         
23247         
23248         // hides menus... - so this cant be on a menu...
23249         Roo.menu.MenuMgr.hideAll();
23250
23251         //this.editorsyncValue();
23252     },
23253    
23254     
23255     createFontOptions : function(){
23256         var buf = [], fs = this.fontFamilies, ff, lc;
23257         
23258         
23259         
23260         for(var i = 0, len = fs.length; i< len; i++){
23261             ff = fs[i];
23262             lc = ff.toLowerCase();
23263             buf.push(
23264                 '<option value="',lc,'" style="font-family:',ff,';"',
23265                     (this.defaultFont == lc ? ' selected="true">' : '>'),
23266                     ff,
23267                 '</option>'
23268             );
23269         }
23270         return buf.join('');
23271     },
23272     
23273     toggleSourceEdit : function(sourceEditMode){
23274         
23275         Roo.log("toolbar toogle");
23276         if(sourceEditMode === undefined){
23277             sourceEditMode = !this.sourceEditMode;
23278         }
23279         this.sourceEditMode = sourceEditMode === true;
23280         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23281         // just toggle the button?
23282         if(btn.pressed !== this.sourceEditMode){
23283             btn.toggle(this.sourceEditMode);
23284             return;
23285         }
23286         
23287         if(sourceEditMode){
23288             Roo.log("disabling buttons");
23289             this.tb.items.each(function(item){
23290                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23291                     item.disable();
23292                 }
23293             });
23294           
23295         }else{
23296             Roo.log("enabling buttons");
23297             if(this.editorcore.initialized){
23298                 this.tb.items.each(function(item){
23299                     item.enable();
23300                 });
23301             }
23302             
23303         }
23304         Roo.log("calling toggole on editor");
23305         // tell the editor that it's been pressed..
23306         this.editor.toggleSourceEdit(sourceEditMode);
23307        
23308     },
23309      /**
23310      * Object collection of toolbar tooltips for the buttons in the editor. The key
23311      * is the command id associated with that button and the value is a valid QuickTips object.
23312      * For example:
23313 <pre><code>
23314 {
23315     bold : {
23316         title: 'Bold (Ctrl+B)',
23317         text: 'Make the selected text bold.',
23318         cls: 'x-html-editor-tip'
23319     },
23320     italic : {
23321         title: 'Italic (Ctrl+I)',
23322         text: 'Make the selected text italic.',
23323         cls: 'x-html-editor-tip'
23324     },
23325     ...
23326 </code></pre>
23327     * @type Object
23328      */
23329     buttonTips : {
23330         bold : {
23331             title: 'Bold (Ctrl+B)',
23332             text: 'Make the selected text bold.',
23333             cls: 'x-html-editor-tip'
23334         },
23335         italic : {
23336             title: 'Italic (Ctrl+I)',
23337             text: 'Make the selected text italic.',
23338             cls: 'x-html-editor-tip'
23339         },
23340         underline : {
23341             title: 'Underline (Ctrl+U)',
23342             text: 'Underline the selected text.',
23343             cls: 'x-html-editor-tip'
23344         },
23345         strikethrough : {
23346             title: 'Strikethrough',
23347             text: 'Strikethrough the selected text.',
23348             cls: 'x-html-editor-tip'
23349         },
23350         increasefontsize : {
23351             title: 'Grow Text',
23352             text: 'Increase the font size.',
23353             cls: 'x-html-editor-tip'
23354         },
23355         decreasefontsize : {
23356             title: 'Shrink Text',
23357             text: 'Decrease the font size.',
23358             cls: 'x-html-editor-tip'
23359         },
23360         backcolor : {
23361             title: 'Text Highlight Color',
23362             text: 'Change the background color of the selected text.',
23363             cls: 'x-html-editor-tip'
23364         },
23365         forecolor : {
23366             title: 'Font Color',
23367             text: 'Change the color of the selected text.',
23368             cls: 'x-html-editor-tip'
23369         },
23370         justifyleft : {
23371             title: 'Align Text Left',
23372             text: 'Align text to the left.',
23373             cls: 'x-html-editor-tip'
23374         },
23375         justifycenter : {
23376             title: 'Center Text',
23377             text: 'Center text in the editor.',
23378             cls: 'x-html-editor-tip'
23379         },
23380         justifyright : {
23381             title: 'Align Text Right',
23382             text: 'Align text to the right.',
23383             cls: 'x-html-editor-tip'
23384         },
23385         insertunorderedlist : {
23386             title: 'Bullet List',
23387             text: 'Start a bulleted list.',
23388             cls: 'x-html-editor-tip'
23389         },
23390         insertorderedlist : {
23391             title: 'Numbered List',
23392             text: 'Start a numbered list.',
23393             cls: 'x-html-editor-tip'
23394         },
23395         createlink : {
23396             title: 'Hyperlink',
23397             text: 'Make the selected text a hyperlink.',
23398             cls: 'x-html-editor-tip'
23399         },
23400         sourceedit : {
23401             title: 'Source Edit',
23402             text: 'Switch to source editing mode.',
23403             cls: 'x-html-editor-tip'
23404         }
23405     },
23406     // private
23407     onDestroy : function(){
23408         if(this.rendered){
23409             
23410             this.tb.items.each(function(item){
23411                 if(item.menu){
23412                     item.menu.removeAll();
23413                     if(item.menu.el){
23414                         item.menu.el.destroy();
23415                     }
23416                 }
23417                 item.destroy();
23418             });
23419              
23420         }
23421     },
23422     onFirstFocus: function() {
23423         this.tb.items.each(function(item){
23424            item.enable();
23425         });
23426     }
23427 });
23428
23429
23430
23431
23432 // <script type="text/javascript">
23433 /*
23434  * Based on
23435  * Ext JS Library 1.1.1
23436  * Copyright(c) 2006-2007, Ext JS, LLC.
23437  *  
23438  
23439  */
23440
23441  
23442 /**
23443  * @class Roo.form.HtmlEditor.ToolbarContext
23444  * Context Toolbar
23445  * 
23446  * Usage:
23447  *
23448  new Roo.form.HtmlEditor({
23449     ....
23450     toolbars : [
23451         { xtype: 'ToolbarStandard', styles : {} }
23452         { xtype: 'ToolbarContext', disable : {} }
23453     ]
23454 })
23455
23456      
23457  * 
23458  * @config : {Object} disable List of elements to disable.. (not done yet.)
23459  * @config : {Object} styles  Map of styles available.
23460  * 
23461  */
23462
23463 Roo.form.HtmlEditor.ToolbarContext = function(config)
23464 {
23465     
23466     Roo.apply(this, config);
23467     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23468     // dont call parent... till later.
23469     this.styles = this.styles || {};
23470 }
23471
23472  
23473
23474 Roo.form.HtmlEditor.ToolbarContext.types = {
23475     'IMG' : {
23476         width : {
23477             title: "Width",
23478             width: 40
23479         },
23480         height:  {
23481             title: "Height",
23482             width: 40
23483         },
23484         align: {
23485             title: "Align",
23486             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
23487             width : 80
23488             
23489         },
23490         border: {
23491             title: "Border",
23492             width: 40
23493         },
23494         alt: {
23495             title: "Alt",
23496             width: 120
23497         },
23498         src : {
23499             title: "Src",
23500             width: 220
23501         }
23502         
23503     },
23504     'A' : {
23505         name : {
23506             title: "Name",
23507             width: 50
23508         },
23509         target:  {
23510             title: "Target",
23511             width: 120
23512         },
23513         href:  {
23514             title: "Href",
23515             width: 220
23516         } // border?
23517         
23518     },
23519     'TABLE' : {
23520         rows : {
23521             title: "Rows",
23522             width: 20
23523         },
23524         cols : {
23525             title: "Cols",
23526             width: 20
23527         },
23528         width : {
23529             title: "Width",
23530             width: 40
23531         },
23532         height : {
23533             title: "Height",
23534             width: 40
23535         },
23536         border : {
23537             title: "Border",
23538             width: 20
23539         }
23540     },
23541     'TD' : {
23542         width : {
23543             title: "Width",
23544             width: 40
23545         },
23546         height : {
23547             title: "Height",
23548             width: 40
23549         },   
23550         align: {
23551             title: "Align",
23552             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
23553             width: 80
23554         },
23555         valign: {
23556             title: "Valign",
23557             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
23558             width: 80
23559         },
23560         colspan: {
23561             title: "Colspan",
23562             width: 20
23563             
23564         },
23565          'font-family'  : {
23566             title : "Font",
23567             style : 'fontFamily',
23568             displayField: 'display',
23569             optname : 'font-family',
23570             width: 140
23571         }
23572     },
23573     'INPUT' : {
23574         name : {
23575             title: "name",
23576             width: 120
23577         },
23578         value : {
23579             title: "Value",
23580             width: 120
23581         },
23582         width : {
23583             title: "Width",
23584             width: 40
23585         }
23586     },
23587     'LABEL' : {
23588         'for' : {
23589             title: "For",
23590             width: 120
23591         }
23592     },
23593     'TEXTAREA' : {
23594           name : {
23595             title: "name",
23596             width: 120
23597         },
23598         rows : {
23599             title: "Rows",
23600             width: 20
23601         },
23602         cols : {
23603             title: "Cols",
23604             width: 20
23605         }
23606     },
23607     'SELECT' : {
23608         name : {
23609             title: "name",
23610             width: 120
23611         },
23612         selectoptions : {
23613             title: "Options",
23614             width: 200
23615         }
23616     },
23617     
23618     // should we really allow this??
23619     // should this just be 
23620     'BODY' : {
23621         title : {
23622             title: "Title",
23623             width: 200,
23624             disabled : true
23625         }
23626     },
23627     'SPAN' : {
23628         'font-family'  : {
23629             title : "Font",
23630             style : 'fontFamily',
23631             displayField: 'display',
23632             optname : 'font-family',
23633             width: 140
23634         }
23635     },
23636     'DIV' : {
23637         'font-family'  : {
23638             title : "Font",
23639             style : 'fontFamily',
23640             displayField: 'display',
23641             optname : 'font-family',
23642             width: 140
23643         }
23644     },
23645      'P' : {
23646         'font-family'  : {
23647             title : "Font",
23648             style : 'fontFamily',
23649             displayField: 'display',
23650             optname : 'font-family',
23651             width: 140
23652         }
23653     },
23654     
23655     '*' : {
23656         // empty..
23657     }
23658
23659 };
23660
23661 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
23662 Roo.form.HtmlEditor.ToolbarContext.stores = false;
23663
23664 Roo.form.HtmlEditor.ToolbarContext.options = {
23665         'font-family'  : [ 
23666                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
23667                 [ 'Courier New', 'Courier New'],
23668                 [ 'Tahoma', 'Tahoma'],
23669                 [ 'Times New Roman,serif', 'Times'],
23670                 [ 'Verdana','Verdana' ]
23671         ]
23672 };
23673
23674 // fixme - these need to be configurable..
23675  
23676
23677 //Roo.form.HtmlEditor.ToolbarContext.types
23678
23679
23680 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
23681     
23682     tb: false,
23683     
23684     rendered: false,
23685     
23686     editor : false,
23687     editorcore : false,
23688     /**
23689      * @cfg {Object} disable  List of toolbar elements to disable
23690          
23691      */
23692     disable : false,
23693     /**
23694      * @cfg {Object} styles List of styles 
23695      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
23696      *
23697      * These must be defined in the page, so they get rendered correctly..
23698      * .headline { }
23699      * TD.underline { }
23700      * 
23701      */
23702     styles : false,
23703     
23704     options: false,
23705     
23706     toolbars : false,
23707     
23708     init : function(editor)
23709     {
23710         this.editor = editor;
23711         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23712         var editorcore = this.editorcore;
23713         
23714         var fid = editorcore.frameId;
23715         var etb = this;
23716         function btn(id, toggle, handler){
23717             var xid = fid + '-'+ id ;
23718             return {
23719                 id : xid,
23720                 cmd : id,
23721                 cls : 'x-btn-icon x-edit-'+id,
23722                 enableToggle:toggle !== false,
23723                 scope: editorcore, // was editor...
23724                 handler:handler||editorcore.relayBtnCmd,
23725                 clickEvent:'mousedown',
23726                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23727                 tabIndex:-1
23728             };
23729         }
23730         // create a new element.
23731         var wdiv = editor.wrap.createChild({
23732                 tag: 'div'
23733             }, editor.wrap.dom.firstChild.nextSibling, true);
23734         
23735         // can we do this more than once??
23736         
23737          // stop form submits
23738       
23739  
23740         // disable everything...
23741         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23742         this.toolbars = {};
23743            
23744         for (var i in  ty) {
23745           
23746             this.toolbars[i] = this.buildToolbar(ty[i],i);
23747         }
23748         this.tb = this.toolbars.BODY;
23749         this.tb.el.show();
23750         this.buildFooter();
23751         this.footer.show();
23752         editor.on('hide', function( ) { this.footer.hide() }, this);
23753         editor.on('show', function( ) { this.footer.show() }, this);
23754         
23755          
23756         this.rendered = true;
23757         
23758         // the all the btns;
23759         editor.on('editorevent', this.updateToolbar, this);
23760         // other toolbars need to implement this..
23761         //editor.on('editmodechange', this.updateToolbar, this);
23762     },
23763     
23764     
23765     
23766     /**
23767      * Protected method that will not generally be called directly. It triggers
23768      * a toolbar update by reading the markup state of the current selection in the editor.
23769      *
23770      * Note you can force an update by calling on('editorevent', scope, false)
23771      */
23772     updateToolbar: function(editor,ev,sel){
23773
23774         //Roo.log(ev);
23775         // capture mouse up - this is handy for selecting images..
23776         // perhaps should go somewhere else...
23777         if(!this.editorcore.activated){
23778              this.editor.onFirstFocus();
23779             return;
23780         }
23781         
23782         
23783         
23784         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
23785         // selectNode - might want to handle IE?
23786         if (ev &&
23787             (ev.type == 'mouseup' || ev.type == 'click' ) &&
23788             ev.target && ev.target.tagName == 'IMG') {
23789             // they have click on an image...
23790             // let's see if we can change the selection...
23791             sel = ev.target;
23792          
23793               var nodeRange = sel.ownerDocument.createRange();
23794             try {
23795                 nodeRange.selectNode(sel);
23796             } catch (e) {
23797                 nodeRange.selectNodeContents(sel);
23798             }
23799             //nodeRange.collapse(true);
23800             var s = this.editorcore.win.getSelection();
23801             s.removeAllRanges();
23802             s.addRange(nodeRange);
23803         }  
23804         
23805       
23806         var updateFooter = sel ? false : true;
23807         
23808         
23809         var ans = this.editorcore.getAllAncestors();
23810         
23811         // pick
23812         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23813         
23814         if (!sel) { 
23815             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
23816             sel = sel ? sel : this.editorcore.doc.body;
23817             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
23818             
23819         }
23820         // pick a menu that exists..
23821         var tn = sel.tagName.toUpperCase();
23822         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
23823         
23824         tn = sel.tagName.toUpperCase();
23825         
23826         var lastSel = this.tb.selectedNode;
23827         
23828         this.tb.selectedNode = sel;
23829         
23830         // if current menu does not match..
23831         
23832         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
23833                 
23834             this.tb.el.hide();
23835             ///console.log("show: " + tn);
23836             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
23837             this.tb.el.show();
23838             // update name
23839             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
23840             
23841             
23842             // update attributes
23843             if (this.tb.fields) {
23844                 this.tb.fields.each(function(e) {
23845                     if (e.stylename) {
23846                         e.setValue(sel.style[e.stylename]);
23847                         return;
23848                     } 
23849                    e.setValue(sel.getAttribute(e.attrname));
23850                 });
23851             }
23852             
23853             var hasStyles = false;
23854             for(var i in this.styles) {
23855                 hasStyles = true;
23856                 break;
23857             }
23858             
23859             // update styles
23860             if (hasStyles) { 
23861                 var st = this.tb.fields.item(0);
23862                 
23863                 st.store.removeAll();
23864                
23865                 
23866                 var cn = sel.className.split(/\s+/);
23867                 
23868                 var avs = [];
23869                 if (this.styles['*']) {
23870                     
23871                     Roo.each(this.styles['*'], function(v) {
23872                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
23873                     });
23874                 }
23875                 if (this.styles[tn]) { 
23876                     Roo.each(this.styles[tn], function(v) {
23877                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
23878                     });
23879                 }
23880                 
23881                 st.store.loadData(avs);
23882                 st.collapse();
23883                 st.setValue(cn);
23884             }
23885             // flag our selected Node.
23886             this.tb.selectedNode = sel;
23887            
23888            
23889             Roo.menu.MenuMgr.hideAll();
23890
23891         }
23892         
23893         if (!updateFooter) {
23894             //this.footDisp.dom.innerHTML = ''; 
23895             return;
23896         }
23897         // update the footer
23898         //
23899         var html = '';
23900         
23901         this.footerEls = ans.reverse();
23902         Roo.each(this.footerEls, function(a,i) {
23903             if (!a) { return; }
23904             html += html.length ? ' &gt; '  :  '';
23905             
23906             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
23907             
23908         });
23909        
23910         // 
23911         var sz = this.footDisp.up('td').getSize();
23912         this.footDisp.dom.style.width = (sz.width -10) + 'px';
23913         this.footDisp.dom.style.marginLeft = '5px';
23914         
23915         this.footDisp.dom.style.overflow = 'hidden';
23916         
23917         this.footDisp.dom.innerHTML = html;
23918             
23919         //this.editorsyncValue();
23920     },
23921      
23922     
23923    
23924        
23925     // private
23926     onDestroy : function(){
23927         if(this.rendered){
23928             
23929             this.tb.items.each(function(item){
23930                 if(item.menu){
23931                     item.menu.removeAll();
23932                     if(item.menu.el){
23933                         item.menu.el.destroy();
23934                     }
23935                 }
23936                 item.destroy();
23937             });
23938              
23939         }
23940     },
23941     onFirstFocus: function() {
23942         // need to do this for all the toolbars..
23943         this.tb.items.each(function(item){
23944            item.enable();
23945         });
23946     },
23947     buildToolbar: function(tlist, nm)
23948     {
23949         var editor = this.editor;
23950         var editorcore = this.editorcore;
23951          // create a new element.
23952         var wdiv = editor.wrap.createChild({
23953                 tag: 'div'
23954             }, editor.wrap.dom.firstChild.nextSibling, true);
23955         
23956        
23957         var tb = new Roo.Toolbar(wdiv);
23958         // add the name..
23959         
23960         tb.add(nm+ ":&nbsp;");
23961         
23962         var styles = [];
23963         for(var i in this.styles) {
23964             styles.push(i);
23965         }
23966         
23967         // styles...
23968         if (styles && styles.length) {
23969             
23970             // this needs a multi-select checkbox...
23971             tb.addField( new Roo.form.ComboBox({
23972                 store: new Roo.data.SimpleStore({
23973                     id : 'val',
23974                     fields: ['val', 'selected'],
23975                     data : [] 
23976                 }),
23977                 name : '-roo-edit-className',
23978                 attrname : 'className',
23979                 displayField: 'val',
23980                 typeAhead: false,
23981                 mode: 'local',
23982                 editable : false,
23983                 triggerAction: 'all',
23984                 emptyText:'Select Style',
23985                 selectOnFocus:true,
23986                 width: 130,
23987                 listeners : {
23988                     'select': function(c, r, i) {
23989                         // initial support only for on class per el..
23990                         tb.selectedNode.className =  r ? r.get('val') : '';
23991                         editorcore.syncValue();
23992                     }
23993                 }
23994     
23995             }));
23996         }
23997         
23998         var tbc = Roo.form.HtmlEditor.ToolbarContext;
23999         var tbops = tbc.options;
24000         
24001         for (var i in tlist) {
24002             
24003             var item = tlist[i];
24004             tb.add(item.title + ":&nbsp;");
24005             
24006             
24007             //optname == used so you can configure the options available..
24008             var opts = item.opts ? item.opts : false;
24009             if (item.optname) {
24010                 opts = tbops[item.optname];
24011            
24012             }
24013             
24014             if (opts) {
24015                 // opts == pulldown..
24016                 tb.addField( new Roo.form.ComboBox({
24017                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24018                         id : 'val',
24019                         fields: ['val', 'display'],
24020                         data : opts  
24021                     }),
24022                     name : '-roo-edit-' + i,
24023                     attrname : i,
24024                     stylename : item.style ? item.style : false,
24025                     displayField: item.displayField ? item.displayField : 'val',
24026                     valueField :  'val',
24027                     typeAhead: false,
24028                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
24029                     editable : false,
24030                     triggerAction: 'all',
24031                     emptyText:'Select',
24032                     selectOnFocus:true,
24033                     width: item.width ? item.width  : 130,
24034                     listeners : {
24035                         'select': function(c, r, i) {
24036                             if (c.stylename) {
24037                                 tb.selectedNode.style[c.stylename] =  r.get('val');
24038                                 return;
24039                             }
24040                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24041                         }
24042                     }
24043
24044                 }));
24045                 continue;
24046                     
24047                  
24048                 
24049                 tb.addField( new Roo.form.TextField({
24050                     name: i,
24051                     width: 100,
24052                     //allowBlank:false,
24053                     value: ''
24054                 }));
24055                 continue;
24056             }
24057             tb.addField( new Roo.form.TextField({
24058                 name: '-roo-edit-' + i,
24059                 attrname : i,
24060                 
24061                 width: item.width,
24062                 //allowBlank:true,
24063                 value: '',
24064                 listeners: {
24065                     'change' : function(f, nv, ov) {
24066                         tb.selectedNode.setAttribute(f.attrname, nv);
24067                         editorcore.syncValue();
24068                     }
24069                 }
24070             }));
24071              
24072         }
24073         
24074         var _this = this;
24075         
24076         if(nm == 'BODY'){
24077             tb.addSeparator();
24078         
24079             tb.addButton( {
24080                 text: 'Stylesheets',
24081
24082                 listeners : {
24083                     click : function ()
24084                     {
24085                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
24086                     }
24087                 }
24088             });
24089         }
24090         
24091         tb.addFill();
24092         tb.addButton( {
24093             text: 'Remove Tag',
24094     
24095             listeners : {
24096                 click : function ()
24097                 {
24098                     // remove
24099                     // undo does not work.
24100                      
24101                     var sn = tb.selectedNode;
24102                     
24103                     var pn = sn.parentNode;
24104                     
24105                     var stn =  sn.childNodes[0];
24106                     var en = sn.childNodes[sn.childNodes.length - 1 ];
24107                     while (sn.childNodes.length) {
24108                         var node = sn.childNodes[0];
24109                         sn.removeChild(node);
24110                         //Roo.log(node);
24111                         pn.insertBefore(node, sn);
24112                         
24113                     }
24114                     pn.removeChild(sn);
24115                     var range = editorcore.createRange();
24116         
24117                     range.setStart(stn,0);
24118                     range.setEnd(en,0); //????
24119                     //range.selectNode(sel);
24120                     
24121                     
24122                     var selection = editorcore.getSelection();
24123                     selection.removeAllRanges();
24124                     selection.addRange(range);
24125                     
24126                     
24127                     
24128                     //_this.updateToolbar(null, null, pn);
24129                     _this.updateToolbar(null, null, null);
24130                     _this.footDisp.dom.innerHTML = ''; 
24131                 }
24132             }
24133             
24134                     
24135                 
24136             
24137         });
24138         
24139         
24140         tb.el.on('click', function(e){
24141             e.preventDefault(); // what does this do?
24142         });
24143         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24144         tb.el.hide();
24145         tb.name = nm;
24146         // dont need to disable them... as they will get hidden
24147         return tb;
24148          
24149         
24150     },
24151     buildFooter : function()
24152     {
24153         
24154         var fel = this.editor.wrap.createChild();
24155         this.footer = new Roo.Toolbar(fel);
24156         // toolbar has scrolly on left / right?
24157         var footDisp= new Roo.Toolbar.Fill();
24158         var _t = this;
24159         this.footer.add(
24160             {
24161                 text : '&lt;',
24162                 xtype: 'Button',
24163                 handler : function() {
24164                     _t.footDisp.scrollTo('left',0,true)
24165                 }
24166             }
24167         );
24168         this.footer.add( footDisp );
24169         this.footer.add( 
24170             {
24171                 text : '&gt;',
24172                 xtype: 'Button',
24173                 handler : function() {
24174                     // no animation..
24175                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24176                 }
24177             }
24178         );
24179         var fel = Roo.get(footDisp.el);
24180         fel.addClass('x-editor-context');
24181         this.footDispWrap = fel; 
24182         this.footDispWrap.overflow  = 'hidden';
24183         
24184         this.footDisp = fel.createChild();
24185         this.footDispWrap.on('click', this.onContextClick, this)
24186         
24187         
24188     },
24189     onContextClick : function (ev,dom)
24190     {
24191         ev.preventDefault();
24192         var  cn = dom.className;
24193         //Roo.log(cn);
24194         if (!cn.match(/x-ed-loc-/)) {
24195             return;
24196         }
24197         var n = cn.split('-').pop();
24198         var ans = this.footerEls;
24199         var sel = ans[n];
24200         
24201          // pick
24202         var range = this.editorcore.createRange();
24203         
24204         range.selectNodeContents(sel);
24205         //range.selectNode(sel);
24206         
24207         
24208         var selection = this.editorcore.getSelection();
24209         selection.removeAllRanges();
24210         selection.addRange(range);
24211         
24212         
24213         
24214         this.updateToolbar(null, null, sel);
24215         
24216         
24217     }
24218     
24219     
24220     
24221     
24222     
24223 });
24224
24225
24226
24227
24228
24229 /*
24230  * Based on:
24231  * Ext JS Library 1.1.1
24232  * Copyright(c) 2006-2007, Ext JS, LLC.
24233  *
24234  * Originally Released Under LGPL - original licence link has changed is not relivant.
24235  *
24236  * Fork - LGPL
24237  * <script type="text/javascript">
24238  */
24239  
24240 /**
24241  * @class Roo.form.BasicForm
24242  * @extends Roo.util.Observable
24243  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24244  * @constructor
24245  * @param {String/HTMLElement/Roo.Element} el The form element or its id
24246  * @param {Object} config Configuration options
24247  */
24248 Roo.form.BasicForm = function(el, config){
24249     this.allItems = [];
24250     this.childForms = [];
24251     Roo.apply(this, config);
24252     /*
24253      * The Roo.form.Field items in this form.
24254      * @type MixedCollection
24255      */
24256      
24257      
24258     this.items = new Roo.util.MixedCollection(false, function(o){
24259         return o.id || (o.id = Roo.id());
24260     });
24261     this.addEvents({
24262         /**
24263          * @event beforeaction
24264          * Fires before any action is performed. Return false to cancel the action.
24265          * @param {Form} this
24266          * @param {Action} action The action to be performed
24267          */
24268         beforeaction: true,
24269         /**
24270          * @event actionfailed
24271          * Fires when an action fails.
24272          * @param {Form} this
24273          * @param {Action} action The action that failed
24274          */
24275         actionfailed : true,
24276         /**
24277          * @event actioncomplete
24278          * Fires when an action is completed.
24279          * @param {Form} this
24280          * @param {Action} action The action that completed
24281          */
24282         actioncomplete : true
24283     });
24284     if(el){
24285         this.initEl(el);
24286     }
24287     Roo.form.BasicForm.superclass.constructor.call(this);
24288 };
24289
24290 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24291     /**
24292      * @cfg {String} method
24293      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24294      */
24295     /**
24296      * @cfg {DataReader} reader
24297      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24298      * This is optional as there is built-in support for processing JSON.
24299      */
24300     /**
24301      * @cfg {DataReader} errorReader
24302      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24303      * This is completely optional as there is built-in support for processing JSON.
24304      */
24305     /**
24306      * @cfg {String} url
24307      * The URL to use for form actions if one isn't supplied in the action options.
24308      */
24309     /**
24310      * @cfg {Boolean} fileUpload
24311      * Set to true if this form is a file upload.
24312      */
24313      
24314     /**
24315      * @cfg {Object} baseParams
24316      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24317      */
24318      /**
24319      
24320     /**
24321      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24322      */
24323     timeout: 30,
24324
24325     // private
24326     activeAction : null,
24327
24328     /**
24329      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24330      * or setValues() data instead of when the form was first created.
24331      */
24332     trackResetOnLoad : false,
24333     
24334     
24335     /**
24336      * childForms - used for multi-tab forms
24337      * @type {Array}
24338      */
24339     childForms : false,
24340     
24341     /**
24342      * allItems - full list of fields.
24343      * @type {Array}
24344      */
24345     allItems : false,
24346     
24347     /**
24348      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24349      * element by passing it or its id or mask the form itself by passing in true.
24350      * @type Mixed
24351      */
24352     waitMsgTarget : false,
24353
24354     // private
24355     initEl : function(el){
24356         this.el = Roo.get(el);
24357         this.id = this.el.id || Roo.id();
24358         this.el.on('submit', this.onSubmit, this);
24359         this.el.addClass('x-form');
24360     },
24361
24362     // private
24363     onSubmit : function(e){
24364         e.stopEvent();
24365     },
24366
24367     /**
24368      * Returns true if client-side validation on the form is successful.
24369      * @return Boolean
24370      */
24371     isValid : function(){
24372         var valid = true;
24373         this.items.each(function(f){
24374            if(!f.validate()){
24375                valid = false;
24376            }
24377         });
24378         return valid;
24379     },
24380
24381     /**
24382      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
24383      * @return Boolean
24384      */
24385     isDirty : function(){
24386         var dirty = false;
24387         this.items.each(function(f){
24388            if(f.isDirty()){
24389                dirty = true;
24390                return false;
24391            }
24392         });
24393         return dirty;
24394     },
24395     
24396     /**
24397      * Returns true if any fields in this form have changed since their original load. (New version)
24398      * @return Boolean
24399      */
24400     
24401     hasChanged : function()
24402     {
24403         var dirty = false;
24404         this.items.each(function(f){
24405            if(f.hasChanged()){
24406                dirty = true;
24407                return false;
24408            }
24409         });
24410         return dirty;
24411         
24412     },
24413     /**
24414      * Resets all hasChanged to 'false' -
24415      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24416      * So hasChanged storage is only to be used for this purpose
24417      * @return Boolean
24418      */
24419     resetHasChanged : function()
24420     {
24421         this.items.each(function(f){
24422            f.resetHasChanged();
24423         });
24424         
24425     },
24426     
24427     
24428     /**
24429      * Performs a predefined action (submit or load) or custom actions you define on this form.
24430      * @param {String} actionName The name of the action type
24431      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
24432      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24433      * accept other config options):
24434      * <pre>
24435 Property          Type             Description
24436 ----------------  ---------------  ----------------------------------------------------------------------------------
24437 url               String           The url for the action (defaults to the form's url)
24438 method            String           The form method to use (defaults to the form's method, or POST if not defined)
24439 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
24440 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
24441                                    validate the form on the client (defaults to false)
24442      * </pre>
24443      * @return {BasicForm} this
24444      */
24445     doAction : function(action, options){
24446         if(typeof action == 'string'){
24447             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
24448         }
24449         if(this.fireEvent('beforeaction', this, action) !== false){
24450             this.beforeAction(action);
24451             action.run.defer(100, action);
24452         }
24453         return this;
24454     },
24455
24456     /**
24457      * Shortcut to do a submit action.
24458      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24459      * @return {BasicForm} this
24460      */
24461     submit : function(options){
24462         this.doAction('submit', options);
24463         return this;
24464     },
24465
24466     /**
24467      * Shortcut to do a load action.
24468      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24469      * @return {BasicForm} this
24470      */
24471     load : function(options){
24472         this.doAction('load', options);
24473         return this;
24474     },
24475
24476     /**
24477      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
24478      * @param {Record} record The record to edit
24479      * @return {BasicForm} this
24480      */
24481     updateRecord : function(record){
24482         record.beginEdit();
24483         var fs = record.fields;
24484         fs.each(function(f){
24485             var field = this.findField(f.name);
24486             if(field){
24487                 record.set(f.name, field.getValue());
24488             }
24489         }, this);
24490         record.endEdit();
24491         return this;
24492     },
24493
24494     /**
24495      * Loads an Roo.data.Record into this form.
24496      * @param {Record} record The record to load
24497      * @return {BasicForm} this
24498      */
24499     loadRecord : function(record){
24500         this.setValues(record.data);
24501         return this;
24502     },
24503
24504     // private
24505     beforeAction : function(action){
24506         var o = action.options;
24507         
24508        
24509         if(this.waitMsgTarget === true){
24510             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
24511         }else if(this.waitMsgTarget){
24512             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
24513             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
24514         }else {
24515             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
24516         }
24517          
24518     },
24519
24520     // private
24521     afterAction : function(action, success){
24522         this.activeAction = null;
24523         var o = action.options;
24524         
24525         if(this.waitMsgTarget === true){
24526             this.el.unmask();
24527         }else if(this.waitMsgTarget){
24528             this.waitMsgTarget.unmask();
24529         }else{
24530             Roo.MessageBox.updateProgress(1);
24531             Roo.MessageBox.hide();
24532         }
24533          
24534         if(success){
24535             if(o.reset){
24536                 this.reset();
24537             }
24538             Roo.callback(o.success, o.scope, [this, action]);
24539             this.fireEvent('actioncomplete', this, action);
24540             
24541         }else{
24542             
24543             // failure condition..
24544             // we have a scenario where updates need confirming.
24545             // eg. if a locking scenario exists..
24546             // we look for { errors : { needs_confirm : true }} in the response.
24547             if (
24548                 (typeof(action.result) != 'undefined')  &&
24549                 (typeof(action.result.errors) != 'undefined')  &&
24550                 (typeof(action.result.errors.needs_confirm) != 'undefined')
24551            ){
24552                 var _t = this;
24553                 Roo.MessageBox.confirm(
24554                     "Change requires confirmation",
24555                     action.result.errorMsg,
24556                     function(r) {
24557                         if (r != 'yes') {
24558                             return;
24559                         }
24560                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
24561                     }
24562                     
24563                 );
24564                 
24565                 
24566                 
24567                 return;
24568             }
24569             
24570             Roo.callback(o.failure, o.scope, [this, action]);
24571             // show an error message if no failed handler is set..
24572             if (!this.hasListener('actionfailed')) {
24573                 Roo.MessageBox.alert("Error",
24574                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
24575                         action.result.errorMsg :
24576                         "Saving Failed, please check your entries or try again"
24577                 );
24578             }
24579             
24580             this.fireEvent('actionfailed', this, action);
24581         }
24582         
24583     },
24584
24585     /**
24586      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
24587      * @param {String} id The value to search for
24588      * @return Field
24589      */
24590     findField : function(id){
24591         var field = this.items.get(id);
24592         if(!field){
24593             this.items.each(function(f){
24594                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
24595                     field = f;
24596                     return false;
24597                 }
24598             });
24599         }
24600         return field || null;
24601     },
24602
24603     /**
24604      * Add a secondary form to this one, 
24605      * Used to provide tabbed forms. One form is primary, with hidden values 
24606      * which mirror the elements from the other forms.
24607      * 
24608      * @param {Roo.form.Form} form to add.
24609      * 
24610      */
24611     addForm : function(form)
24612     {
24613        
24614         if (this.childForms.indexOf(form) > -1) {
24615             // already added..
24616             return;
24617         }
24618         this.childForms.push(form);
24619         var n = '';
24620         Roo.each(form.allItems, function (fe) {
24621             
24622             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
24623             if (this.findField(n)) { // already added..
24624                 return;
24625             }
24626             var add = new Roo.form.Hidden({
24627                 name : n
24628             });
24629             add.render(this.el);
24630             
24631             this.add( add );
24632         }, this);
24633         
24634     },
24635     /**
24636      * Mark fields in this form invalid in bulk.
24637      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
24638      * @return {BasicForm} this
24639      */
24640     markInvalid : function(errors){
24641         if(errors instanceof Array){
24642             for(var i = 0, len = errors.length; i < len; i++){
24643                 var fieldError = errors[i];
24644                 var f = this.findField(fieldError.id);
24645                 if(f){
24646                     f.markInvalid(fieldError.msg);
24647                 }
24648             }
24649         }else{
24650             var field, id;
24651             for(id in errors){
24652                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
24653                     field.markInvalid(errors[id]);
24654                 }
24655             }
24656         }
24657         Roo.each(this.childForms || [], function (f) {
24658             f.markInvalid(errors);
24659         });
24660         
24661         return this;
24662     },
24663
24664     /**
24665      * Set values for fields in this form in bulk.
24666      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
24667      * @return {BasicForm} this
24668      */
24669     setValues : function(values){
24670         if(values instanceof Array){ // array of objects
24671             for(var i = 0, len = values.length; i < len; i++){
24672                 var v = values[i];
24673                 var f = this.findField(v.id);
24674                 if(f){
24675                     f.setValue(v.value);
24676                     if(this.trackResetOnLoad){
24677                         f.originalValue = f.getValue();
24678                     }
24679                 }
24680             }
24681         }else{ // object hash
24682             var field, id;
24683             for(id in values){
24684                 if(typeof values[id] != 'function' && (field = this.findField(id))){
24685                     
24686                     if (field.setFromData && 
24687                         field.valueField && 
24688                         field.displayField &&
24689                         // combos' with local stores can 
24690                         // be queried via setValue()
24691                         // to set their value..
24692                         (field.store && !field.store.isLocal)
24693                         ) {
24694                         // it's a combo
24695                         var sd = { };
24696                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
24697                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
24698                         field.setFromData(sd);
24699                         
24700                     } else {
24701                         field.setValue(values[id]);
24702                     }
24703                     
24704                     
24705                     if(this.trackResetOnLoad){
24706                         field.originalValue = field.getValue();
24707                     }
24708                 }
24709             }
24710         }
24711         this.resetHasChanged();
24712         
24713         
24714         Roo.each(this.childForms || [], function (f) {
24715             f.setValues(values);
24716             f.resetHasChanged();
24717         });
24718                 
24719         return this;
24720     },
24721
24722     /**
24723      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
24724      * they are returned as an array.
24725      * @param {Boolean} asString
24726      * @return {Object}
24727      */
24728     getValues : function(asString){
24729         if (this.childForms) {
24730             // copy values from the child forms
24731             Roo.each(this.childForms, function (f) {
24732                 this.setValues(f.getValues());
24733             }, this);
24734         }
24735         
24736         
24737         
24738         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
24739         if(asString === true){
24740             return fs;
24741         }
24742         return Roo.urlDecode(fs);
24743     },
24744     
24745     /**
24746      * Returns the fields in this form as an object with key/value pairs. 
24747      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
24748      * @return {Object}
24749      */
24750     getFieldValues : function(with_hidden)
24751     {
24752         if (this.childForms) {
24753             // copy values from the child forms
24754             // should this call getFieldValues - probably not as we do not currently copy
24755             // hidden fields when we generate..
24756             Roo.each(this.childForms, function (f) {
24757                 this.setValues(f.getValues());
24758             }, this);
24759         }
24760         
24761         var ret = {};
24762         this.items.each(function(f){
24763             if (!f.getName()) {
24764                 return;
24765             }
24766             var v = f.getValue();
24767             if (f.inputType =='radio') {
24768                 if (typeof(ret[f.getName()]) == 'undefined') {
24769                     ret[f.getName()] = ''; // empty..
24770                 }
24771                 
24772                 if (!f.el.dom.checked) {
24773                     return;
24774                     
24775                 }
24776                 v = f.el.dom.value;
24777                 
24778             }
24779             
24780             // not sure if this supported any more..
24781             if ((typeof(v) == 'object') && f.getRawValue) {
24782                 v = f.getRawValue() ; // dates..
24783             }
24784             // combo boxes where name != hiddenName...
24785             if (f.name != f.getName()) {
24786                 ret[f.name] = f.getRawValue();
24787             }
24788             ret[f.getName()] = v;
24789         });
24790         
24791         return ret;
24792     },
24793
24794     /**
24795      * Clears all invalid messages in this form.
24796      * @return {BasicForm} this
24797      */
24798     clearInvalid : function(){
24799         this.items.each(function(f){
24800            f.clearInvalid();
24801         });
24802         
24803         Roo.each(this.childForms || [], function (f) {
24804             f.clearInvalid();
24805         });
24806         
24807         
24808         return this;
24809     },
24810
24811     /**
24812      * Resets this form.
24813      * @return {BasicForm} this
24814      */
24815     reset : function(){
24816         this.items.each(function(f){
24817             f.reset();
24818         });
24819         
24820         Roo.each(this.childForms || [], function (f) {
24821             f.reset();
24822         });
24823         this.resetHasChanged();
24824         
24825         return this;
24826     },
24827
24828     /**
24829      * Add Roo.form components to this form.
24830      * @param {Field} field1
24831      * @param {Field} field2 (optional)
24832      * @param {Field} etc (optional)
24833      * @return {BasicForm} this
24834      */
24835     add : function(){
24836         this.items.addAll(Array.prototype.slice.call(arguments, 0));
24837         return this;
24838     },
24839
24840
24841     /**
24842      * Removes a field from the items collection (does NOT remove its markup).
24843      * @param {Field} field
24844      * @return {BasicForm} this
24845      */
24846     remove : function(field){
24847         this.items.remove(field);
24848         return this;
24849     },
24850
24851     /**
24852      * Looks at the fields in this form, checks them for an id attribute,
24853      * and calls applyTo on the existing dom element with that id.
24854      * @return {BasicForm} this
24855      */
24856     render : function(){
24857         this.items.each(function(f){
24858             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
24859                 f.applyTo(f.id);
24860             }
24861         });
24862         return this;
24863     },
24864
24865     /**
24866      * Calls {@link Ext#apply} for all fields in this form with the passed object.
24867      * @param {Object} values
24868      * @return {BasicForm} this
24869      */
24870     applyToFields : function(o){
24871         this.items.each(function(f){
24872            Roo.apply(f, o);
24873         });
24874         return this;
24875     },
24876
24877     /**
24878      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
24879      * @param {Object} values
24880      * @return {BasicForm} this
24881      */
24882     applyIfToFields : function(o){
24883         this.items.each(function(f){
24884            Roo.applyIf(f, o);
24885         });
24886         return this;
24887     }
24888 });
24889
24890 // back compat
24891 Roo.BasicForm = Roo.form.BasicForm;/*
24892  * Based on:
24893  * Ext JS Library 1.1.1
24894  * Copyright(c) 2006-2007, Ext JS, LLC.
24895  *
24896  * Originally Released Under LGPL - original licence link has changed is not relivant.
24897  *
24898  * Fork - LGPL
24899  * <script type="text/javascript">
24900  */
24901
24902 /**
24903  * @class Roo.form.Form
24904  * @extends Roo.form.BasicForm
24905  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
24906  * @constructor
24907  * @param {Object} config Configuration options
24908  */
24909 Roo.form.Form = function(config){
24910     var xitems =  [];
24911     if (config.items) {
24912         xitems = config.items;
24913         delete config.items;
24914     }
24915    
24916     
24917     Roo.form.Form.superclass.constructor.call(this, null, config);
24918     this.url = this.url || this.action;
24919     if(!this.root){
24920         this.root = new Roo.form.Layout(Roo.applyIf({
24921             id: Roo.id()
24922         }, config));
24923     }
24924     this.active = this.root;
24925     /**
24926      * Array of all the buttons that have been added to this form via {@link addButton}
24927      * @type Array
24928      */
24929     this.buttons = [];
24930     this.allItems = [];
24931     this.addEvents({
24932         /**
24933          * @event clientvalidation
24934          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
24935          * @param {Form} this
24936          * @param {Boolean} valid true if the form has passed client-side validation
24937          */
24938         clientvalidation: true,
24939         /**
24940          * @event rendered
24941          * Fires when the form is rendered
24942          * @param {Roo.form.Form} form
24943          */
24944         rendered : true
24945     });
24946     
24947     if (this.progressUrl) {
24948             // push a hidden field onto the list of fields..
24949             this.addxtype( {
24950                     xns: Roo.form, 
24951                     xtype : 'Hidden', 
24952                     name : 'UPLOAD_IDENTIFIER' 
24953             });
24954         }
24955         
24956     
24957     Roo.each(xitems, this.addxtype, this);
24958     
24959     
24960     
24961 };
24962
24963 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
24964     /**
24965      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
24966      */
24967     /**
24968      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
24969      */
24970     /**
24971      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
24972      */
24973     buttonAlign:'center',
24974
24975     /**
24976      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
24977      */
24978     minButtonWidth:75,
24979
24980     /**
24981      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
24982      * This property cascades to child containers if not set.
24983      */
24984     labelAlign:'left',
24985
24986     /**
24987      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
24988      * fires a looping event with that state. This is required to bind buttons to the valid
24989      * state using the config value formBind:true on the button.
24990      */
24991     monitorValid : false,
24992
24993     /**
24994      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
24995      */
24996     monitorPoll : 200,
24997     
24998     /**
24999      * @cfg {String} progressUrl - Url to return progress data 
25000      */
25001     
25002     progressUrl : false,
25003   
25004     /**
25005      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25006      * fields are added and the column is closed. If no fields are passed the column remains open
25007      * until end() is called.
25008      * @param {Object} config The config to pass to the column
25009      * @param {Field} field1 (optional)
25010      * @param {Field} field2 (optional)
25011      * @param {Field} etc (optional)
25012      * @return Column The column container object
25013      */
25014     column : function(c){
25015         var col = new Roo.form.Column(c);
25016         this.start(col);
25017         if(arguments.length > 1){ // duplicate code required because of Opera
25018             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25019             this.end();
25020         }
25021         return col;
25022     },
25023
25024     /**
25025      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25026      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25027      * until end() is called.
25028      * @param {Object} config The config to pass to the fieldset
25029      * @param {Field} field1 (optional)
25030      * @param {Field} field2 (optional)
25031      * @param {Field} etc (optional)
25032      * @return FieldSet The fieldset container object
25033      */
25034     fieldset : function(c){
25035         var fs = new Roo.form.FieldSet(c);
25036         this.start(fs);
25037         if(arguments.length > 1){ // duplicate code required because of Opera
25038             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25039             this.end();
25040         }
25041         return fs;
25042     },
25043
25044     /**
25045      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25046      * fields are added and the container is closed. If no fields are passed the container remains open
25047      * until end() is called.
25048      * @param {Object} config The config to pass to the Layout
25049      * @param {Field} field1 (optional)
25050      * @param {Field} field2 (optional)
25051      * @param {Field} etc (optional)
25052      * @return Layout The container object
25053      */
25054     container : function(c){
25055         var l = new Roo.form.Layout(c);
25056         this.start(l);
25057         if(arguments.length > 1){ // duplicate code required because of Opera
25058             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25059             this.end();
25060         }
25061         return l;
25062     },
25063
25064     /**
25065      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25066      * @param {Object} container A Roo.form.Layout or subclass of Layout
25067      * @return {Form} this
25068      */
25069     start : function(c){
25070         // cascade label info
25071         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25072         this.active.stack.push(c);
25073         c.ownerCt = this.active;
25074         this.active = c;
25075         return this;
25076     },
25077
25078     /**
25079      * Closes the current open container
25080      * @return {Form} this
25081      */
25082     end : function(){
25083         if(this.active == this.root){
25084             return this;
25085         }
25086         this.active = this.active.ownerCt;
25087         return this;
25088     },
25089
25090     /**
25091      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25092      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25093      * as the label of the field.
25094      * @param {Field} field1
25095      * @param {Field} field2 (optional)
25096      * @param {Field} etc. (optional)
25097      * @return {Form} this
25098      */
25099     add : function(){
25100         this.active.stack.push.apply(this.active.stack, arguments);
25101         this.allItems.push.apply(this.allItems,arguments);
25102         var r = [];
25103         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25104             if(a[i].isFormField){
25105                 r.push(a[i]);
25106             }
25107         }
25108         if(r.length > 0){
25109             Roo.form.Form.superclass.add.apply(this, r);
25110         }
25111         return this;
25112     },
25113     
25114
25115     
25116     
25117     
25118      /**
25119      * Find any element that has been added to a form, using it's ID or name
25120      * This can include framesets, columns etc. along with regular fields..
25121      * @param {String} id - id or name to find.
25122      
25123      * @return {Element} e - or false if nothing found.
25124      */
25125     findbyId : function(id)
25126     {
25127         var ret = false;
25128         if (!id) {
25129             return ret;
25130         }
25131         Roo.each(this.allItems, function(f){
25132             if (f.id == id || f.name == id ){
25133                 ret = f;
25134                 return false;
25135             }
25136         });
25137         return ret;
25138     },
25139
25140     
25141     
25142     /**
25143      * Render this form into the passed container. This should only be called once!
25144      * @param {String/HTMLElement/Element} container The element this component should be rendered into
25145      * @return {Form} this
25146      */
25147     render : function(ct)
25148     {
25149         
25150         
25151         
25152         ct = Roo.get(ct);
25153         var o = this.autoCreate || {
25154             tag: 'form',
25155             method : this.method || 'POST',
25156             id : this.id || Roo.id()
25157         };
25158         this.initEl(ct.createChild(o));
25159
25160         this.root.render(this.el);
25161         
25162        
25163              
25164         this.items.each(function(f){
25165             f.render('x-form-el-'+f.id);
25166         });
25167
25168         if(this.buttons.length > 0){
25169             // tables are required to maintain order and for correct IE layout
25170             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25171                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25172                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25173             }}, null, true);
25174             var tr = tb.getElementsByTagName('tr')[0];
25175             for(var i = 0, len = this.buttons.length; i < len; i++) {
25176                 var b = this.buttons[i];
25177                 var td = document.createElement('td');
25178                 td.className = 'x-form-btn-td';
25179                 b.render(tr.appendChild(td));
25180             }
25181         }
25182         if(this.monitorValid){ // initialize after render
25183             this.startMonitoring();
25184         }
25185         this.fireEvent('rendered', this);
25186         return this;
25187     },
25188
25189     /**
25190      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25191      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25192      * object or a valid Roo.DomHelper element config
25193      * @param {Function} handler The function called when the button is clicked
25194      * @param {Object} scope (optional) The scope of the handler function
25195      * @return {Roo.Button}
25196      */
25197     addButton : function(config, handler, scope){
25198         var bc = {
25199             handler: handler,
25200             scope: scope,
25201             minWidth: this.minButtonWidth,
25202             hideParent:true
25203         };
25204         if(typeof config == "string"){
25205             bc.text = config;
25206         }else{
25207             Roo.apply(bc, config);
25208         }
25209         var btn = new Roo.Button(null, bc);
25210         this.buttons.push(btn);
25211         return btn;
25212     },
25213
25214      /**
25215      * Adds a series of form elements (using the xtype property as the factory method.
25216      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25217      * @param {Object} config 
25218      */
25219     
25220     addxtype : function()
25221     {
25222         var ar = Array.prototype.slice.call(arguments, 0);
25223         var ret = false;
25224         for(var i = 0; i < ar.length; i++) {
25225             if (!ar[i]) {
25226                 continue; // skip -- if this happends something invalid got sent, we 
25227                 // should ignore it, as basically that interface element will not show up
25228                 // and that should be pretty obvious!!
25229             }
25230             
25231             if (Roo.form[ar[i].xtype]) {
25232                 ar[i].form = this;
25233                 var fe = Roo.factory(ar[i], Roo.form);
25234                 if (!ret) {
25235                     ret = fe;
25236                 }
25237                 fe.form = this;
25238                 if (fe.store) {
25239                     fe.store.form = this;
25240                 }
25241                 if (fe.isLayout) {  
25242                          
25243                     this.start(fe);
25244                     this.allItems.push(fe);
25245                     if (fe.items && fe.addxtype) {
25246                         fe.addxtype.apply(fe, fe.items);
25247                         delete fe.items;
25248                     }
25249                      this.end();
25250                     continue;
25251                 }
25252                 
25253                 
25254                  
25255                 this.add(fe);
25256               //  console.log('adding ' + ar[i].xtype);
25257             }
25258             if (ar[i].xtype == 'Button') {  
25259                 //console.log('adding button');
25260                 //console.log(ar[i]);
25261                 this.addButton(ar[i]);
25262                 this.allItems.push(fe);
25263                 continue;
25264             }
25265             
25266             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25267                 alert('end is not supported on xtype any more, use items');
25268             //    this.end();
25269             //    //console.log('adding end');
25270             }
25271             
25272         }
25273         return ret;
25274     },
25275     
25276     /**
25277      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
25278      * option "monitorValid"
25279      */
25280     startMonitoring : function(){
25281         if(!this.bound){
25282             this.bound = true;
25283             Roo.TaskMgr.start({
25284                 run : this.bindHandler,
25285                 interval : this.monitorPoll || 200,
25286                 scope: this
25287             });
25288         }
25289     },
25290
25291     /**
25292      * Stops monitoring of the valid state of this form
25293      */
25294     stopMonitoring : function(){
25295         this.bound = false;
25296     },
25297
25298     // private
25299     bindHandler : function(){
25300         if(!this.bound){
25301             return false; // stops binding
25302         }
25303         var valid = true;
25304         this.items.each(function(f){
25305             if(!f.isValid(true)){
25306                 valid = false;
25307                 return false;
25308             }
25309         });
25310         for(var i = 0, len = this.buttons.length; i < len; i++){
25311             var btn = this.buttons[i];
25312             if(btn.formBind === true && btn.disabled === valid){
25313                 btn.setDisabled(!valid);
25314             }
25315         }
25316         this.fireEvent('clientvalidation', this, valid);
25317     }
25318     
25319     
25320     
25321     
25322     
25323     
25324     
25325     
25326 });
25327
25328
25329 // back compat
25330 Roo.Form = Roo.form.Form;
25331 /*
25332  * Based on:
25333  * Ext JS Library 1.1.1
25334  * Copyright(c) 2006-2007, Ext JS, LLC.
25335  *
25336  * Originally Released Under LGPL - original licence link has changed is not relivant.
25337  *
25338  * Fork - LGPL
25339  * <script type="text/javascript">
25340  */
25341
25342 // as we use this in bootstrap.
25343 Roo.namespace('Roo.form');
25344  /**
25345  * @class Roo.form.Action
25346  * Internal Class used to handle form actions
25347  * @constructor
25348  * @param {Roo.form.BasicForm} el The form element or its id
25349  * @param {Object} config Configuration options
25350  */
25351
25352  
25353  
25354 // define the action interface
25355 Roo.form.Action = function(form, options){
25356     this.form = form;
25357     this.options = options || {};
25358 };
25359 /**
25360  * Client Validation Failed
25361  * @const 
25362  */
25363 Roo.form.Action.CLIENT_INVALID = 'client';
25364 /**
25365  * Server Validation Failed
25366  * @const 
25367  */
25368 Roo.form.Action.SERVER_INVALID = 'server';
25369  /**
25370  * Connect to Server Failed
25371  * @const 
25372  */
25373 Roo.form.Action.CONNECT_FAILURE = 'connect';
25374 /**
25375  * Reading Data from Server Failed
25376  * @const 
25377  */
25378 Roo.form.Action.LOAD_FAILURE = 'load';
25379
25380 Roo.form.Action.prototype = {
25381     type : 'default',
25382     failureType : undefined,
25383     response : undefined,
25384     result : undefined,
25385
25386     // interface method
25387     run : function(options){
25388
25389     },
25390
25391     // interface method
25392     success : function(response){
25393
25394     },
25395
25396     // interface method
25397     handleResponse : function(response){
25398
25399     },
25400
25401     // default connection failure
25402     failure : function(response){
25403         
25404         this.response = response;
25405         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25406         this.form.afterAction(this, false);
25407     },
25408
25409     processResponse : function(response){
25410         this.response = response;
25411         if(!response.responseText){
25412             return true;
25413         }
25414         this.result = this.handleResponse(response);
25415         return this.result;
25416     },
25417
25418     // utility functions used internally
25419     getUrl : function(appendParams){
25420         var url = this.options.url || this.form.url || this.form.el.dom.action;
25421         if(appendParams){
25422             var p = this.getParams();
25423             if(p){
25424                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
25425             }
25426         }
25427         return url;
25428     },
25429
25430     getMethod : function(){
25431         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
25432     },
25433
25434     getParams : function(){
25435         var bp = this.form.baseParams;
25436         var p = this.options.params;
25437         if(p){
25438             if(typeof p == "object"){
25439                 p = Roo.urlEncode(Roo.applyIf(p, bp));
25440             }else if(typeof p == 'string' && bp){
25441                 p += '&' + Roo.urlEncode(bp);
25442             }
25443         }else if(bp){
25444             p = Roo.urlEncode(bp);
25445         }
25446         return p;
25447     },
25448
25449     createCallback : function(){
25450         return {
25451             success: this.success,
25452             failure: this.failure,
25453             scope: this,
25454             timeout: (this.form.timeout*1000),
25455             upload: this.form.fileUpload ? this.success : undefined
25456         };
25457     }
25458 };
25459
25460 Roo.form.Action.Submit = function(form, options){
25461     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
25462 };
25463
25464 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
25465     type : 'submit',
25466
25467     haveProgress : false,
25468     uploadComplete : false,
25469     
25470     // uploadProgress indicator.
25471     uploadProgress : function()
25472     {
25473         if (!this.form.progressUrl) {
25474             return;
25475         }
25476         
25477         if (!this.haveProgress) {
25478             Roo.MessageBox.progress("Uploading", "Uploading");
25479         }
25480         if (this.uploadComplete) {
25481            Roo.MessageBox.hide();
25482            return;
25483         }
25484         
25485         this.haveProgress = true;
25486    
25487         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
25488         
25489         var c = new Roo.data.Connection();
25490         c.request({
25491             url : this.form.progressUrl,
25492             params: {
25493                 id : uid
25494             },
25495             method: 'GET',
25496             success : function(req){
25497                //console.log(data);
25498                 var rdata = false;
25499                 var edata;
25500                 try  {
25501                    rdata = Roo.decode(req.responseText)
25502                 } catch (e) {
25503                     Roo.log("Invalid data from server..");
25504                     Roo.log(edata);
25505                     return;
25506                 }
25507                 if (!rdata || !rdata.success) {
25508                     Roo.log(rdata);
25509                     Roo.MessageBox.alert(Roo.encode(rdata));
25510                     return;
25511                 }
25512                 var data = rdata.data;
25513                 
25514                 if (this.uploadComplete) {
25515                    Roo.MessageBox.hide();
25516                    return;
25517                 }
25518                    
25519                 if (data){
25520                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
25521                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
25522                     );
25523                 }
25524                 this.uploadProgress.defer(2000,this);
25525             },
25526        
25527             failure: function(data) {
25528                 Roo.log('progress url failed ');
25529                 Roo.log(data);
25530             },
25531             scope : this
25532         });
25533            
25534     },
25535     
25536     
25537     run : function()
25538     {
25539         // run get Values on the form, so it syncs any secondary forms.
25540         this.form.getValues();
25541         
25542         var o = this.options;
25543         var method = this.getMethod();
25544         var isPost = method == 'POST';
25545         if(o.clientValidation === false || this.form.isValid()){
25546             
25547             if (this.form.progressUrl) {
25548                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
25549                     (new Date() * 1) + '' + Math.random());
25550                     
25551             } 
25552             
25553             
25554             Roo.Ajax.request(Roo.apply(this.createCallback(), {
25555                 form:this.form.el.dom,
25556                 url:this.getUrl(!isPost),
25557                 method: method,
25558                 params:isPost ? this.getParams() : null,
25559                 isUpload: this.form.fileUpload
25560             }));
25561             
25562             this.uploadProgress();
25563
25564         }else if (o.clientValidation !== false){ // client validation failed
25565             this.failureType = Roo.form.Action.CLIENT_INVALID;
25566             this.form.afterAction(this, false);
25567         }
25568     },
25569
25570     success : function(response)
25571     {
25572         this.uploadComplete= true;
25573         if (this.haveProgress) {
25574             Roo.MessageBox.hide();
25575         }
25576         
25577         
25578         var result = this.processResponse(response);
25579         if(result === true || result.success){
25580             this.form.afterAction(this, true);
25581             return;
25582         }
25583         if(result.errors){
25584             this.form.markInvalid(result.errors);
25585             this.failureType = Roo.form.Action.SERVER_INVALID;
25586         }
25587         this.form.afterAction(this, false);
25588     },
25589     failure : function(response)
25590     {
25591         this.uploadComplete= true;
25592         if (this.haveProgress) {
25593             Roo.MessageBox.hide();
25594         }
25595         
25596         this.response = response;
25597         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25598         this.form.afterAction(this, false);
25599     },
25600     
25601     handleResponse : function(response){
25602         if(this.form.errorReader){
25603             var rs = this.form.errorReader.read(response);
25604             var errors = [];
25605             if(rs.records){
25606                 for(var i = 0, len = rs.records.length; i < len; i++) {
25607                     var r = rs.records[i];
25608                     errors[i] = r.data;
25609                 }
25610             }
25611             if(errors.length < 1){
25612                 errors = null;
25613             }
25614             return {
25615                 success : rs.success,
25616                 errors : errors
25617             };
25618         }
25619         var ret = false;
25620         try {
25621             ret = Roo.decode(response.responseText);
25622         } catch (e) {
25623             ret = {
25624                 success: false,
25625                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
25626                 errors : []
25627             };
25628         }
25629         return ret;
25630         
25631     }
25632 });
25633
25634
25635 Roo.form.Action.Load = function(form, options){
25636     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
25637     this.reader = this.form.reader;
25638 };
25639
25640 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
25641     type : 'load',
25642
25643     run : function(){
25644         
25645         Roo.Ajax.request(Roo.apply(
25646                 this.createCallback(), {
25647                     method:this.getMethod(),
25648                     url:this.getUrl(false),
25649                     params:this.getParams()
25650         }));
25651     },
25652
25653     success : function(response){
25654         
25655         var result = this.processResponse(response);
25656         if(result === true || !result.success || !result.data){
25657             this.failureType = Roo.form.Action.LOAD_FAILURE;
25658             this.form.afterAction(this, false);
25659             return;
25660         }
25661         this.form.clearInvalid();
25662         this.form.setValues(result.data);
25663         this.form.afterAction(this, true);
25664     },
25665
25666     handleResponse : function(response){
25667         if(this.form.reader){
25668             var rs = this.form.reader.read(response);
25669             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
25670             return {
25671                 success : rs.success,
25672                 data : data
25673             };
25674         }
25675         return Roo.decode(response.responseText);
25676     }
25677 });
25678
25679 Roo.form.Action.ACTION_TYPES = {
25680     'load' : Roo.form.Action.Load,
25681     'submit' : Roo.form.Action.Submit
25682 };/*
25683  * Based on:
25684  * Ext JS Library 1.1.1
25685  * Copyright(c) 2006-2007, Ext JS, LLC.
25686  *
25687  * Originally Released Under LGPL - original licence link has changed is not relivant.
25688  *
25689  * Fork - LGPL
25690  * <script type="text/javascript">
25691  */
25692  
25693 /**
25694  * @class Roo.form.Layout
25695  * @extends Roo.Component
25696  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
25697  * @constructor
25698  * @param {Object} config Configuration options
25699  */
25700 Roo.form.Layout = function(config){
25701     var xitems = [];
25702     if (config.items) {
25703         xitems = config.items;
25704         delete config.items;
25705     }
25706     Roo.form.Layout.superclass.constructor.call(this, config);
25707     this.stack = [];
25708     Roo.each(xitems, this.addxtype, this);
25709      
25710 };
25711
25712 Roo.extend(Roo.form.Layout, Roo.Component, {
25713     /**
25714      * @cfg {String/Object} autoCreate
25715      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
25716      */
25717     /**
25718      * @cfg {String/Object/Function} style
25719      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
25720      * a function which returns such a specification.
25721      */
25722     /**
25723      * @cfg {String} labelAlign
25724      * Valid values are "left," "top" and "right" (defaults to "left")
25725      */
25726     /**
25727      * @cfg {Number} labelWidth
25728      * Fixed width in pixels of all field labels (defaults to undefined)
25729      */
25730     /**
25731      * @cfg {Boolean} clear
25732      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
25733      */
25734     clear : true,
25735     /**
25736      * @cfg {String} labelSeparator
25737      * The separator to use after field labels (defaults to ':')
25738      */
25739     labelSeparator : ':',
25740     /**
25741      * @cfg {Boolean} hideLabels
25742      * True to suppress the display of field labels in this layout (defaults to false)
25743      */
25744     hideLabels : false,
25745
25746     // private
25747     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
25748     
25749     isLayout : true,
25750     
25751     // private
25752     onRender : function(ct, position){
25753         if(this.el){ // from markup
25754             this.el = Roo.get(this.el);
25755         }else {  // generate
25756             var cfg = this.getAutoCreate();
25757             this.el = ct.createChild(cfg, position);
25758         }
25759         if(this.style){
25760             this.el.applyStyles(this.style);
25761         }
25762         if(this.labelAlign){
25763             this.el.addClass('x-form-label-'+this.labelAlign);
25764         }
25765         if(this.hideLabels){
25766             this.labelStyle = "display:none";
25767             this.elementStyle = "padding-left:0;";
25768         }else{
25769             if(typeof this.labelWidth == 'number'){
25770                 this.labelStyle = "width:"+this.labelWidth+"px;";
25771                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
25772             }
25773             if(this.labelAlign == 'top'){
25774                 this.labelStyle = "width:auto;";
25775                 this.elementStyle = "padding-left:0;";
25776             }
25777         }
25778         var stack = this.stack;
25779         var slen = stack.length;
25780         if(slen > 0){
25781             if(!this.fieldTpl){
25782                 var t = new Roo.Template(
25783                     '<div class="x-form-item {5}">',
25784                         '<label for="{0}" style="{2}">{1}{4}</label>',
25785                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
25786                         '</div>',
25787                     '</div><div class="x-form-clear-left"></div>'
25788                 );
25789                 t.disableFormats = true;
25790                 t.compile();
25791                 Roo.form.Layout.prototype.fieldTpl = t;
25792             }
25793             for(var i = 0; i < slen; i++) {
25794                 if(stack[i].isFormField){
25795                     this.renderField(stack[i]);
25796                 }else{
25797                     this.renderComponent(stack[i]);
25798                 }
25799             }
25800         }
25801         if(this.clear){
25802             this.el.createChild({cls:'x-form-clear'});
25803         }
25804     },
25805
25806     // private
25807     renderField : function(f){
25808         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
25809                f.id, //0
25810                f.fieldLabel, //1
25811                f.labelStyle||this.labelStyle||'', //2
25812                this.elementStyle||'', //3
25813                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
25814                f.itemCls||this.itemCls||''  //5
25815        ], true).getPrevSibling());
25816     },
25817
25818     // private
25819     renderComponent : function(c){
25820         c.render(c.isLayout ? this.el : this.el.createChild());    
25821     },
25822     /**
25823      * Adds a object form elements (using the xtype property as the factory method.)
25824      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
25825      * @param {Object} config 
25826      */
25827     addxtype : function(o)
25828     {
25829         // create the lement.
25830         o.form = this.form;
25831         var fe = Roo.factory(o, Roo.form);
25832         this.form.allItems.push(fe);
25833         this.stack.push(fe);
25834         
25835         if (fe.isFormField) {
25836             this.form.items.add(fe);
25837         }
25838          
25839         return fe;
25840     }
25841 });
25842
25843 /**
25844  * @class Roo.form.Column
25845  * @extends Roo.form.Layout
25846  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
25847  * @constructor
25848  * @param {Object} config Configuration options
25849  */
25850 Roo.form.Column = function(config){
25851     Roo.form.Column.superclass.constructor.call(this, config);
25852 };
25853
25854 Roo.extend(Roo.form.Column, Roo.form.Layout, {
25855     /**
25856      * @cfg {Number/String} width
25857      * The fixed width of the column in pixels or CSS value (defaults to "auto")
25858      */
25859     /**
25860      * @cfg {String/Object} autoCreate
25861      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
25862      */
25863
25864     // private
25865     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
25866
25867     // private
25868     onRender : function(ct, position){
25869         Roo.form.Column.superclass.onRender.call(this, ct, position);
25870         if(this.width){
25871             this.el.setWidth(this.width);
25872         }
25873     }
25874 });
25875
25876
25877 /**
25878  * @class Roo.form.Row
25879  * @extends Roo.form.Layout
25880  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
25881  * @constructor
25882  * @param {Object} config Configuration options
25883  */
25884
25885  
25886 Roo.form.Row = function(config){
25887     Roo.form.Row.superclass.constructor.call(this, config);
25888 };
25889  
25890 Roo.extend(Roo.form.Row, Roo.form.Layout, {
25891       /**
25892      * @cfg {Number/String} width
25893      * The fixed width of the column in pixels or CSS value (defaults to "auto")
25894      */
25895     /**
25896      * @cfg {Number/String} height
25897      * The fixed height of the column in pixels or CSS value (defaults to "auto")
25898      */
25899     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
25900     
25901     padWidth : 20,
25902     // private
25903     onRender : function(ct, position){
25904         //console.log('row render');
25905         if(!this.rowTpl){
25906             var t = new Roo.Template(
25907                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
25908                     '<label for="{0}" style="{2}">{1}{4}</label>',
25909                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
25910                     '</div>',
25911                 '</div>'
25912             );
25913             t.disableFormats = true;
25914             t.compile();
25915             Roo.form.Layout.prototype.rowTpl = t;
25916         }
25917         this.fieldTpl = this.rowTpl;
25918         
25919         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
25920         var labelWidth = 100;
25921         
25922         if ((this.labelAlign != 'top')) {
25923             if (typeof this.labelWidth == 'number') {
25924                 labelWidth = this.labelWidth
25925             }
25926             this.padWidth =  20 + labelWidth;
25927             
25928         }
25929         
25930         Roo.form.Column.superclass.onRender.call(this, ct, position);
25931         if(this.width){
25932             this.el.setWidth(this.width);
25933         }
25934         if(this.height){
25935             this.el.setHeight(this.height);
25936         }
25937     },
25938     
25939     // private
25940     renderField : function(f){
25941         f.fieldEl = this.fieldTpl.append(this.el, [
25942                f.id, f.fieldLabel,
25943                f.labelStyle||this.labelStyle||'',
25944                this.elementStyle||'',
25945                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
25946                f.itemCls||this.itemCls||'',
25947                f.width ? f.width + this.padWidth : 160 + this.padWidth
25948        ],true);
25949     }
25950 });
25951  
25952
25953 /**
25954  * @class Roo.form.FieldSet
25955  * @extends Roo.form.Layout
25956  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
25957  * @constructor
25958  * @param {Object} config Configuration options
25959  */
25960 Roo.form.FieldSet = function(config){
25961     Roo.form.FieldSet.superclass.constructor.call(this, config);
25962 };
25963
25964 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
25965     /**
25966      * @cfg {String} legend
25967      * The text to display as the legend for the FieldSet (defaults to '')
25968      */
25969     /**
25970      * @cfg {String/Object} autoCreate
25971      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
25972      */
25973
25974     // private
25975     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
25976
25977     // private
25978     onRender : function(ct, position){
25979         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
25980         if(this.legend){
25981             this.setLegend(this.legend);
25982         }
25983     },
25984
25985     // private
25986     setLegend : function(text){
25987         if(this.rendered){
25988             this.el.child('legend').update(text);
25989         }
25990     }
25991 });/*
25992  * Based on:
25993  * Ext JS Library 1.1.1
25994  * Copyright(c) 2006-2007, Ext JS, LLC.
25995  *
25996  * Originally Released Under LGPL - original licence link has changed is not relivant.
25997  *
25998  * Fork - LGPL
25999  * <script type="text/javascript">
26000  */
26001 /**
26002  * @class Roo.form.VTypes
26003  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26004  * @singleton
26005  */
26006 Roo.form.VTypes = function(){
26007     // closure these in so they are only created once.
26008     var alpha = /^[a-zA-Z_]+$/;
26009     var alphanum = /^[a-zA-Z0-9_]+$/;
26010     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26011     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26012
26013     // All these messages and functions are configurable
26014     return {
26015         /**
26016          * The function used to validate email addresses
26017          * @param {String} value The email address
26018          */
26019         'email' : function(v){
26020             return email.test(v);
26021         },
26022         /**
26023          * The error text to display when the email validation function returns false
26024          * @type String
26025          */
26026         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26027         /**
26028          * The keystroke filter mask to be applied on email input
26029          * @type RegExp
26030          */
26031         'emailMask' : /[a-z0-9_\.\-@]/i,
26032
26033         /**
26034          * The function used to validate URLs
26035          * @param {String} value The URL
26036          */
26037         'url' : function(v){
26038             return url.test(v);
26039         },
26040         /**
26041          * The error text to display when the url validation function returns false
26042          * @type String
26043          */
26044         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26045         
26046         /**
26047          * The function used to validate alpha values
26048          * @param {String} value The value
26049          */
26050         'alpha' : function(v){
26051             return alpha.test(v);
26052         },
26053         /**
26054          * The error text to display when the alpha validation function returns false
26055          * @type String
26056          */
26057         'alphaText' : 'This field should only contain letters and _',
26058         /**
26059          * The keystroke filter mask to be applied on alpha input
26060          * @type RegExp
26061          */
26062         'alphaMask' : /[a-z_]/i,
26063
26064         /**
26065          * The function used to validate alphanumeric values
26066          * @param {String} value The value
26067          */
26068         'alphanum' : function(v){
26069             return alphanum.test(v);
26070         },
26071         /**
26072          * The error text to display when the alphanumeric validation function returns false
26073          * @type String
26074          */
26075         'alphanumText' : 'This field should only contain letters, numbers and _',
26076         /**
26077          * The keystroke filter mask to be applied on alphanumeric input
26078          * @type RegExp
26079          */
26080         'alphanumMask' : /[a-z0-9_]/i
26081     };
26082 }();//<script type="text/javascript">
26083
26084 /**
26085  * @class Roo.form.FCKeditor
26086  * @extends Roo.form.TextArea
26087  * Wrapper around the FCKEditor http://www.fckeditor.net
26088  * @constructor
26089  * Creates a new FCKeditor
26090  * @param {Object} config Configuration options
26091  */
26092 Roo.form.FCKeditor = function(config){
26093     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26094     this.addEvents({
26095          /**
26096          * @event editorinit
26097          * Fired when the editor is initialized - you can add extra handlers here..
26098          * @param {FCKeditor} this
26099          * @param {Object} the FCK object.
26100          */
26101         editorinit : true
26102     });
26103     
26104     
26105 };
26106 Roo.form.FCKeditor.editors = { };
26107 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26108 {
26109     //defaultAutoCreate : {
26110     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26111     //},
26112     // private
26113     /**
26114      * @cfg {Object} fck options - see fck manual for details.
26115      */
26116     fckconfig : false,
26117     
26118     /**
26119      * @cfg {Object} fck toolbar set (Basic or Default)
26120      */
26121     toolbarSet : 'Basic',
26122     /**
26123      * @cfg {Object} fck BasePath
26124      */ 
26125     basePath : '/fckeditor/',
26126     
26127     
26128     frame : false,
26129     
26130     value : '',
26131     
26132    
26133     onRender : function(ct, position)
26134     {
26135         if(!this.el){
26136             this.defaultAutoCreate = {
26137                 tag: "textarea",
26138                 style:"width:300px;height:60px;",
26139                 autocomplete: "new-password"
26140             };
26141         }
26142         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26143         /*
26144         if(this.grow){
26145             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26146             if(this.preventScrollbars){
26147                 this.el.setStyle("overflow", "hidden");
26148             }
26149             this.el.setHeight(this.growMin);
26150         }
26151         */
26152         //console.log('onrender' + this.getId() );
26153         Roo.form.FCKeditor.editors[this.getId()] = this;
26154          
26155
26156         this.replaceTextarea() ;
26157         
26158     },
26159     
26160     getEditor : function() {
26161         return this.fckEditor;
26162     },
26163     /**
26164      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26165      * @param {Mixed} value The value to set
26166      */
26167     
26168     
26169     setValue : function(value)
26170     {
26171         //console.log('setValue: ' + value);
26172         
26173         if(typeof(value) == 'undefined') { // not sure why this is happending...
26174             return;
26175         }
26176         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26177         
26178         //if(!this.el || !this.getEditor()) {
26179         //    this.value = value;
26180             //this.setValue.defer(100,this,[value]);    
26181         //    return;
26182         //} 
26183         
26184         if(!this.getEditor()) {
26185             return;
26186         }
26187         
26188         this.getEditor().SetData(value);
26189         
26190         //
26191
26192     },
26193
26194     /**
26195      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26196      * @return {Mixed} value The field value
26197      */
26198     getValue : function()
26199     {
26200         
26201         if (this.frame && this.frame.dom.style.display == 'none') {
26202             return Roo.form.FCKeditor.superclass.getValue.call(this);
26203         }
26204         
26205         if(!this.el || !this.getEditor()) {
26206            
26207            // this.getValue.defer(100,this); 
26208             return this.value;
26209         }
26210        
26211         
26212         var value=this.getEditor().GetData();
26213         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26214         return Roo.form.FCKeditor.superclass.getValue.call(this);
26215         
26216
26217     },
26218
26219     /**
26220      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26221      * @return {Mixed} value The field value
26222      */
26223     getRawValue : function()
26224     {
26225         if (this.frame && this.frame.dom.style.display == 'none') {
26226             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26227         }
26228         
26229         if(!this.el || !this.getEditor()) {
26230             //this.getRawValue.defer(100,this); 
26231             return this.value;
26232             return;
26233         }
26234         
26235         
26236         
26237         var value=this.getEditor().GetData();
26238         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26239         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26240          
26241     },
26242     
26243     setSize : function(w,h) {
26244         
26245         
26246         
26247         //if (this.frame && this.frame.dom.style.display == 'none') {
26248         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26249         //    return;
26250         //}
26251         //if(!this.el || !this.getEditor()) {
26252         //    this.setSize.defer(100,this, [w,h]); 
26253         //    return;
26254         //}
26255         
26256         
26257         
26258         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26259         
26260         this.frame.dom.setAttribute('width', w);
26261         this.frame.dom.setAttribute('height', h);
26262         this.frame.setSize(w,h);
26263         
26264     },
26265     
26266     toggleSourceEdit : function(value) {
26267         
26268       
26269          
26270         this.el.dom.style.display = value ? '' : 'none';
26271         this.frame.dom.style.display = value ?  'none' : '';
26272         
26273     },
26274     
26275     
26276     focus: function(tag)
26277     {
26278         if (this.frame.dom.style.display == 'none') {
26279             return Roo.form.FCKeditor.superclass.focus.call(this);
26280         }
26281         if(!this.el || !this.getEditor()) {
26282             this.focus.defer(100,this, [tag]); 
26283             return;
26284         }
26285         
26286         
26287         
26288         
26289         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
26290         this.getEditor().Focus();
26291         if (tgs.length) {
26292             if (!this.getEditor().Selection.GetSelection()) {
26293                 this.focus.defer(100,this, [tag]); 
26294                 return;
26295             }
26296             
26297             
26298             var r = this.getEditor().EditorDocument.createRange();
26299             r.setStart(tgs[0],0);
26300             r.setEnd(tgs[0],0);
26301             this.getEditor().Selection.GetSelection().removeAllRanges();
26302             this.getEditor().Selection.GetSelection().addRange(r);
26303             this.getEditor().Focus();
26304         }
26305         
26306     },
26307     
26308     
26309     
26310     replaceTextarea : function()
26311     {
26312         if ( document.getElementById( this.getId() + '___Frame' ) ) {
26313             return ;
26314         }
26315         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
26316         //{
26317             // We must check the elements firstly using the Id and then the name.
26318         var oTextarea = document.getElementById( this.getId() );
26319         
26320         var colElementsByName = document.getElementsByName( this.getId() ) ;
26321          
26322         oTextarea.style.display = 'none' ;
26323
26324         if ( oTextarea.tabIndex ) {            
26325             this.TabIndex = oTextarea.tabIndex ;
26326         }
26327         
26328         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
26329         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
26330         this.frame = Roo.get(this.getId() + '___Frame')
26331     },
26332     
26333     _getConfigHtml : function()
26334     {
26335         var sConfig = '' ;
26336
26337         for ( var o in this.fckconfig ) {
26338             sConfig += sConfig.length > 0  ? '&amp;' : '';
26339             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
26340         }
26341
26342         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
26343     },
26344     
26345     
26346     _getIFrameHtml : function()
26347     {
26348         var sFile = 'fckeditor.html' ;
26349         /* no idea what this is about..
26350         try
26351         {
26352             if ( (/fcksource=true/i).test( window.top.location.search ) )
26353                 sFile = 'fckeditor.original.html' ;
26354         }
26355         catch (e) { 
26356         */
26357
26358         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
26359         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
26360         
26361         
26362         var html = '<iframe id="' + this.getId() +
26363             '___Frame" src="' + sLink +
26364             '" width="' + this.width +
26365             '" height="' + this.height + '"' +
26366             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
26367             ' frameborder="0" scrolling="no"></iframe>' ;
26368
26369         return html ;
26370     },
26371     
26372     _insertHtmlBefore : function( html, element )
26373     {
26374         if ( element.insertAdjacentHTML )       {
26375             // IE
26376             element.insertAdjacentHTML( 'beforeBegin', html ) ;
26377         } else { // Gecko
26378             var oRange = document.createRange() ;
26379             oRange.setStartBefore( element ) ;
26380             var oFragment = oRange.createContextualFragment( html );
26381             element.parentNode.insertBefore( oFragment, element ) ;
26382         }
26383     }
26384     
26385     
26386   
26387     
26388     
26389     
26390     
26391
26392 });
26393
26394 //Roo.reg('fckeditor', Roo.form.FCKeditor);
26395
26396 function FCKeditor_OnComplete(editorInstance){
26397     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
26398     f.fckEditor = editorInstance;
26399     //console.log("loaded");
26400     f.fireEvent('editorinit', f, editorInstance);
26401
26402   
26403
26404  
26405
26406
26407
26408
26409
26410
26411
26412
26413
26414
26415
26416
26417
26418
26419
26420 //<script type="text/javascript">
26421 /**
26422  * @class Roo.form.GridField
26423  * @extends Roo.form.Field
26424  * Embed a grid (or editable grid into a form)
26425  * STATUS ALPHA
26426  * 
26427  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
26428  * it needs 
26429  * xgrid.store = Roo.data.Store
26430  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
26431  * xgrid.store.reader = Roo.data.JsonReader 
26432  * 
26433  * 
26434  * @constructor
26435  * Creates a new GridField
26436  * @param {Object} config Configuration options
26437  */
26438 Roo.form.GridField = function(config){
26439     Roo.form.GridField.superclass.constructor.call(this, config);
26440      
26441 };
26442
26443 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
26444     /**
26445      * @cfg {Number} width  - used to restrict width of grid..
26446      */
26447     width : 100,
26448     /**
26449      * @cfg {Number} height - used to restrict height of grid..
26450      */
26451     height : 50,
26452      /**
26453      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
26454          * 
26455          *}
26456      */
26457     xgrid : false, 
26458     /**
26459      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26460      * {tag: "input", type: "checkbox", autocomplete: "off"})
26461      */
26462    // defaultAutoCreate : { tag: 'div' },
26463     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
26464     /**
26465      * @cfg {String} addTitle Text to include for adding a title.
26466      */
26467     addTitle : false,
26468     //
26469     onResize : function(){
26470         Roo.form.Field.superclass.onResize.apply(this, arguments);
26471     },
26472
26473     initEvents : function(){
26474         // Roo.form.Checkbox.superclass.initEvents.call(this);
26475         // has no events...
26476        
26477     },
26478
26479
26480     getResizeEl : function(){
26481         return this.wrap;
26482     },
26483
26484     getPositionEl : function(){
26485         return this.wrap;
26486     },
26487
26488     // private
26489     onRender : function(ct, position){
26490         
26491         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
26492         var style = this.style;
26493         delete this.style;
26494         
26495         Roo.form.GridField.superclass.onRender.call(this, ct, position);
26496         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
26497         this.viewEl = this.wrap.createChild({ tag: 'div' });
26498         if (style) {
26499             this.viewEl.applyStyles(style);
26500         }
26501         if (this.width) {
26502             this.viewEl.setWidth(this.width);
26503         }
26504         if (this.height) {
26505             this.viewEl.setHeight(this.height);
26506         }
26507         //if(this.inputValue !== undefined){
26508         //this.setValue(this.value);
26509         
26510         
26511         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
26512         
26513         
26514         this.grid.render();
26515         this.grid.getDataSource().on('remove', this.refreshValue, this);
26516         this.grid.getDataSource().on('update', this.refreshValue, this);
26517         this.grid.on('afteredit', this.refreshValue, this);
26518  
26519     },
26520      
26521     
26522     /**
26523      * Sets the value of the item. 
26524      * @param {String} either an object  or a string..
26525      */
26526     setValue : function(v){
26527         //this.value = v;
26528         v = v || []; // empty set..
26529         // this does not seem smart - it really only affects memoryproxy grids..
26530         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
26531             var ds = this.grid.getDataSource();
26532             // assumes a json reader..
26533             var data = {}
26534             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
26535             ds.loadData( data);
26536         }
26537         // clear selection so it does not get stale.
26538         if (this.grid.sm) { 
26539             this.grid.sm.clearSelections();
26540         }
26541         
26542         Roo.form.GridField.superclass.setValue.call(this, v);
26543         this.refreshValue();
26544         // should load data in the grid really....
26545     },
26546     
26547     // private
26548     refreshValue: function() {
26549          var val = [];
26550         this.grid.getDataSource().each(function(r) {
26551             val.push(r.data);
26552         });
26553         this.el.dom.value = Roo.encode(val);
26554     }
26555     
26556      
26557     
26558     
26559 });/*
26560  * Based on:
26561  * Ext JS Library 1.1.1
26562  * Copyright(c) 2006-2007, Ext JS, LLC.
26563  *
26564  * Originally Released Under LGPL - original licence link has changed is not relivant.
26565  *
26566  * Fork - LGPL
26567  * <script type="text/javascript">
26568  */
26569 /**
26570  * @class Roo.form.DisplayField
26571  * @extends Roo.form.Field
26572  * A generic Field to display non-editable data.
26573  * @cfg {Boolean} closable (true|false) default false
26574  * @constructor
26575  * Creates a new Display Field item.
26576  * @param {Object} config Configuration options
26577  */
26578 Roo.form.DisplayField = function(config){
26579     Roo.form.DisplayField.superclass.constructor.call(this, config);
26580     
26581     this.addEvents({
26582         /**
26583          * @event close
26584          * Fires after the click the close btn
26585              * @param {Roo.form.DisplayField} this
26586              */
26587         close : true
26588     });
26589 };
26590
26591 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
26592     inputType:      'hidden',
26593     allowBlank:     true,
26594     readOnly:         true,
26595     
26596  
26597     /**
26598      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
26599      */
26600     focusClass : undefined,
26601     /**
26602      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
26603      */
26604     fieldClass: 'x-form-field',
26605     
26606      /**
26607      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
26608      */
26609     valueRenderer: undefined,
26610     
26611     width: 100,
26612     /**
26613      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26614      * {tag: "input", type: "checkbox", autocomplete: "off"})
26615      */
26616      
26617  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
26618  
26619     closable : false,
26620     
26621     onResize : function(){
26622         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
26623         
26624     },
26625
26626     initEvents : function(){
26627         // Roo.form.Checkbox.superclass.initEvents.call(this);
26628         // has no events...
26629         
26630         if(this.closable){
26631             this.closeEl.on('click', this.onClose, this);
26632         }
26633        
26634     },
26635
26636
26637     getResizeEl : function(){
26638         return this.wrap;
26639     },
26640
26641     getPositionEl : function(){
26642         return this.wrap;
26643     },
26644
26645     // private
26646     onRender : function(ct, position){
26647         
26648         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
26649         //if(this.inputValue !== undefined){
26650         this.wrap = this.el.wrap();
26651         
26652         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
26653         
26654         if(this.closable){
26655             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
26656         }
26657         
26658         if (this.bodyStyle) {
26659             this.viewEl.applyStyles(this.bodyStyle);
26660         }
26661         //this.viewEl.setStyle('padding', '2px');
26662         
26663         this.setValue(this.value);
26664         
26665     },
26666 /*
26667     // private
26668     initValue : Roo.emptyFn,
26669
26670   */
26671
26672         // private
26673     onClick : function(){
26674         
26675     },
26676
26677     /**
26678      * Sets the checked state of the checkbox.
26679      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
26680      */
26681     setValue : function(v){
26682         this.value = v;
26683         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
26684         // this might be called before we have a dom element..
26685         if (!this.viewEl) {
26686             return;
26687         }
26688         this.viewEl.dom.innerHTML = html;
26689         Roo.form.DisplayField.superclass.setValue.call(this, v);
26690
26691     },
26692     
26693     onClose : function(e)
26694     {
26695         e.preventDefault();
26696         
26697         this.fireEvent('close', this);
26698     }
26699 });/*
26700  * 
26701  * Licence- LGPL
26702  * 
26703  */
26704
26705 /**
26706  * @class Roo.form.DayPicker
26707  * @extends Roo.form.Field
26708  * A Day picker show [M] [T] [W] ....
26709  * @constructor
26710  * Creates a new Day Picker
26711  * @param {Object} config Configuration options
26712  */
26713 Roo.form.DayPicker= function(config){
26714     Roo.form.DayPicker.superclass.constructor.call(this, config);
26715      
26716 };
26717
26718 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
26719     /**
26720      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
26721      */
26722     focusClass : undefined,
26723     /**
26724      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
26725      */
26726     fieldClass: "x-form-field",
26727    
26728     /**
26729      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26730      * {tag: "input", type: "checkbox", autocomplete: "off"})
26731      */
26732     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
26733     
26734    
26735     actionMode : 'viewEl', 
26736     //
26737     // private
26738  
26739     inputType : 'hidden',
26740     
26741      
26742     inputElement: false, // real input element?
26743     basedOn: false, // ????
26744     
26745     isFormField: true, // not sure where this is needed!!!!
26746
26747     onResize : function(){
26748         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
26749         if(!this.boxLabel){
26750             this.el.alignTo(this.wrap, 'c-c');
26751         }
26752     },
26753
26754     initEvents : function(){
26755         Roo.form.Checkbox.superclass.initEvents.call(this);
26756         this.el.on("click", this.onClick,  this);
26757         this.el.on("change", this.onClick,  this);
26758     },
26759
26760
26761     getResizeEl : function(){
26762         return this.wrap;
26763     },
26764
26765     getPositionEl : function(){
26766         return this.wrap;
26767     },
26768
26769     
26770     // private
26771     onRender : function(ct, position){
26772         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
26773        
26774         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
26775         
26776         var r1 = '<table><tr>';
26777         var r2 = '<tr class="x-form-daypick-icons">';
26778         for (var i=0; i < 7; i++) {
26779             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
26780             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
26781         }
26782         
26783         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
26784         viewEl.select('img').on('click', this.onClick, this);
26785         this.viewEl = viewEl;   
26786         
26787         
26788         // this will not work on Chrome!!!
26789         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
26790         this.el.on('propertychange', this.setFromHidden,  this);  //ie
26791         
26792         
26793           
26794
26795     },
26796
26797     // private
26798     initValue : Roo.emptyFn,
26799
26800     /**
26801      * Returns the checked state of the checkbox.
26802      * @return {Boolean} True if checked, else false
26803      */
26804     getValue : function(){
26805         return this.el.dom.value;
26806         
26807     },
26808
26809         // private
26810     onClick : function(e){ 
26811         //this.setChecked(!this.checked);
26812         Roo.get(e.target).toggleClass('x-menu-item-checked');
26813         this.refreshValue();
26814         //if(this.el.dom.checked != this.checked){
26815         //    this.setValue(this.el.dom.checked);
26816        // }
26817     },
26818     
26819     // private
26820     refreshValue : function()
26821     {
26822         var val = '';
26823         this.viewEl.select('img',true).each(function(e,i,n)  {
26824             val += e.is(".x-menu-item-checked") ? String(n) : '';
26825         });
26826         this.setValue(val, true);
26827     },
26828
26829     /**
26830      * Sets the checked state of the checkbox.
26831      * On is always based on a string comparison between inputValue and the param.
26832      * @param {Boolean/String} value - the value to set 
26833      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
26834      */
26835     setValue : function(v,suppressEvent){
26836         if (!this.el.dom) {
26837             return;
26838         }
26839         var old = this.el.dom.value ;
26840         this.el.dom.value = v;
26841         if (suppressEvent) {
26842             return ;
26843         }
26844          
26845         // update display..
26846         this.viewEl.select('img',true).each(function(e,i,n)  {
26847             
26848             var on = e.is(".x-menu-item-checked");
26849             var newv = v.indexOf(String(n)) > -1;
26850             if (on != newv) {
26851                 e.toggleClass('x-menu-item-checked');
26852             }
26853             
26854         });
26855         
26856         
26857         this.fireEvent('change', this, v, old);
26858         
26859         
26860     },
26861    
26862     // handle setting of hidden value by some other method!!?!?
26863     setFromHidden: function()
26864     {
26865         if(!this.el){
26866             return;
26867         }
26868         //console.log("SET FROM HIDDEN");
26869         //alert('setFrom hidden');
26870         this.setValue(this.el.dom.value);
26871     },
26872     
26873     onDestroy : function()
26874     {
26875         if(this.viewEl){
26876             Roo.get(this.viewEl).remove();
26877         }
26878          
26879         Roo.form.DayPicker.superclass.onDestroy.call(this);
26880     }
26881
26882 });/*
26883  * RooJS Library 1.1.1
26884  * Copyright(c) 2008-2011  Alan Knowles
26885  *
26886  * License - LGPL
26887  */
26888  
26889
26890 /**
26891  * @class Roo.form.ComboCheck
26892  * @extends Roo.form.ComboBox
26893  * A combobox for multiple select items.
26894  *
26895  * FIXME - could do with a reset button..
26896  * 
26897  * @constructor
26898  * Create a new ComboCheck
26899  * @param {Object} config Configuration options
26900  */
26901 Roo.form.ComboCheck = function(config){
26902     Roo.form.ComboCheck.superclass.constructor.call(this, config);
26903     // should verify some data...
26904     // like
26905     // hiddenName = required..
26906     // displayField = required
26907     // valudField == required
26908     var req= [ 'hiddenName', 'displayField', 'valueField' ];
26909     var _t = this;
26910     Roo.each(req, function(e) {
26911         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
26912             throw "Roo.form.ComboCheck : missing value for: " + e;
26913         }
26914     });
26915     
26916     
26917 };
26918
26919 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
26920      
26921      
26922     editable : false,
26923      
26924     selectedClass: 'x-menu-item-checked', 
26925     
26926     // private
26927     onRender : function(ct, position){
26928         var _t = this;
26929         
26930         
26931         
26932         if(!this.tpl){
26933             var cls = 'x-combo-list';
26934
26935             
26936             this.tpl =  new Roo.Template({
26937                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
26938                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
26939                    '<span>{' + this.displayField + '}</span>' +
26940                     '</div>' 
26941                 
26942             });
26943         }
26944  
26945         
26946         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
26947         this.view.singleSelect = false;
26948         this.view.multiSelect = true;
26949         this.view.toggleSelect = true;
26950         this.pageTb.add(new Roo.Toolbar.Fill(), {
26951             
26952             text: 'Done',
26953             handler: function()
26954             {
26955                 _t.collapse();
26956             }
26957         });
26958     },
26959     
26960     onViewOver : function(e, t){
26961         // do nothing...
26962         return;
26963         
26964     },
26965     
26966     onViewClick : function(doFocus,index){
26967         return;
26968         
26969     },
26970     select: function () {
26971         //Roo.log("SELECT CALLED");
26972     },
26973      
26974     selectByValue : function(xv, scrollIntoView){
26975         var ar = this.getValueArray();
26976         var sels = [];
26977         
26978         Roo.each(ar, function(v) {
26979             if(v === undefined || v === null){
26980                 return;
26981             }
26982             var r = this.findRecord(this.valueField, v);
26983             if(r){
26984                 sels.push(this.store.indexOf(r))
26985                 
26986             }
26987         },this);
26988         this.view.select(sels);
26989         return false;
26990     },
26991     
26992     
26993     
26994     onSelect : function(record, index){
26995        // Roo.log("onselect Called");
26996        // this is only called by the clear button now..
26997         this.view.clearSelections();
26998         this.setValue('[]');
26999         if (this.value != this.valueBefore) {
27000             this.fireEvent('change', this, this.value, this.valueBefore);
27001             this.valueBefore = this.value;
27002         }
27003     },
27004     getValueArray : function()
27005     {
27006         var ar = [] ;
27007         
27008         try {
27009             //Roo.log(this.value);
27010             if (typeof(this.value) == 'undefined') {
27011                 return [];
27012             }
27013             var ar = Roo.decode(this.value);
27014             return  ar instanceof Array ? ar : []; //?? valid?
27015             
27016         } catch(e) {
27017             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
27018             return [];
27019         }
27020          
27021     },
27022     expand : function ()
27023     {
27024         
27025         Roo.form.ComboCheck.superclass.expand.call(this);
27026         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27027         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27028         
27029
27030     },
27031     
27032     collapse : function(){
27033         Roo.form.ComboCheck.superclass.collapse.call(this);
27034         var sl = this.view.getSelectedIndexes();
27035         var st = this.store;
27036         var nv = [];
27037         var tv = [];
27038         var r;
27039         Roo.each(sl, function(i) {
27040             r = st.getAt(i);
27041             nv.push(r.get(this.valueField));
27042         },this);
27043         this.setValue(Roo.encode(nv));
27044         if (this.value != this.valueBefore) {
27045
27046             this.fireEvent('change', this, this.value, this.valueBefore);
27047             this.valueBefore = this.value;
27048         }
27049         
27050     },
27051     
27052     setValue : function(v){
27053         // Roo.log(v);
27054         this.value = v;
27055         
27056         var vals = this.getValueArray();
27057         var tv = [];
27058         Roo.each(vals, function(k) {
27059             var r = this.findRecord(this.valueField, k);
27060             if(r){
27061                 tv.push(r.data[this.displayField]);
27062             }else if(this.valueNotFoundText !== undefined){
27063                 tv.push( this.valueNotFoundText );
27064             }
27065         },this);
27066        // Roo.log(tv);
27067         
27068         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27069         this.hiddenField.value = v;
27070         this.value = v;
27071     }
27072     
27073 });/*
27074  * Based on:
27075  * Ext JS Library 1.1.1
27076  * Copyright(c) 2006-2007, Ext JS, LLC.
27077  *
27078  * Originally Released Under LGPL - original licence link has changed is not relivant.
27079  *
27080  * Fork - LGPL
27081  * <script type="text/javascript">
27082  */
27083  
27084 /**
27085  * @class Roo.form.Signature
27086  * @extends Roo.form.Field
27087  * Signature field.  
27088  * @constructor
27089  * 
27090  * @param {Object} config Configuration options
27091  */
27092
27093 Roo.form.Signature = function(config){
27094     Roo.form.Signature.superclass.constructor.call(this, config);
27095     
27096     this.addEvents({// not in used??
27097          /**
27098          * @event confirm
27099          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27100              * @param {Roo.form.Signature} combo This combo box
27101              */
27102         'confirm' : true,
27103         /**
27104          * @event reset
27105          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27106              * @param {Roo.form.ComboBox} combo This combo box
27107              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27108              */
27109         'reset' : true
27110     });
27111 };
27112
27113 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
27114     /**
27115      * @cfg {Object} labels Label to use when rendering a form.
27116      * defaults to 
27117      * labels : { 
27118      *      clear : "Clear",
27119      *      confirm : "Confirm"
27120      *  }
27121      */
27122     labels : { 
27123         clear : "Clear",
27124         confirm : "Confirm"
27125     },
27126     /**
27127      * @cfg {Number} width The signature panel width (defaults to 300)
27128      */
27129     width: 300,
27130     /**
27131      * @cfg {Number} height The signature panel height (defaults to 100)
27132      */
27133     height : 100,
27134     /**
27135      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27136      */
27137     allowBlank : false,
27138     
27139     //private
27140     // {Object} signPanel The signature SVG panel element (defaults to {})
27141     signPanel : {},
27142     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27143     isMouseDown : false,
27144     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27145     isConfirmed : false,
27146     // {String} signatureTmp SVG mapping string (defaults to empty string)
27147     signatureTmp : '',
27148     
27149     
27150     defaultAutoCreate : { // modified by initCompnoent..
27151         tag: "input",
27152         type:"hidden"
27153     },
27154
27155     // private
27156     onRender : function(ct, position){
27157         
27158         Roo.form.Signature.superclass.onRender.call(this, ct, position);
27159         
27160         this.wrap = this.el.wrap({
27161             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27162         });
27163         
27164         this.createToolbar(this);
27165         this.signPanel = this.wrap.createChild({
27166                 tag: 'div',
27167                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27168             }, this.el
27169         );
27170             
27171         this.svgID = Roo.id();
27172         this.svgEl = this.signPanel.createChild({
27173               xmlns : 'http://www.w3.org/2000/svg',
27174               tag : 'svg',
27175               id : this.svgID + "-svg",
27176               width: this.width,
27177               height: this.height,
27178               viewBox: '0 0 '+this.width+' '+this.height,
27179               cn : [
27180                 {
27181                     tag: "rect",
27182                     id: this.svgID + "-svg-r",
27183                     width: this.width,
27184                     height: this.height,
27185                     fill: "#ffa"
27186                 },
27187                 {
27188                     tag: "line",
27189                     id: this.svgID + "-svg-l",
27190                     x1: "0", // start
27191                     y1: (this.height*0.8), // start set the line in 80% of height
27192                     x2: this.width, // end
27193                     y2: (this.height*0.8), // end set the line in 80% of height
27194                     'stroke': "#666",
27195                     'stroke-width': "1",
27196                     'stroke-dasharray': "3",
27197                     'shape-rendering': "crispEdges",
27198                     'pointer-events': "none"
27199                 },
27200                 {
27201                     tag: "path",
27202                     id: this.svgID + "-svg-p",
27203                     'stroke': "navy",
27204                     'stroke-width': "3",
27205                     'fill': "none",
27206                     'pointer-events': 'none'
27207                 }
27208               ]
27209         });
27210         this.createSVG();
27211         this.svgBox = this.svgEl.dom.getScreenCTM();
27212     },
27213     createSVG : function(){ 
27214         var svg = this.signPanel;
27215         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27216         var t = this;
27217
27218         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27219         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27220         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27221         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27222         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27223         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27224         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27225         
27226     },
27227     isTouchEvent : function(e){
27228         return e.type.match(/^touch/);
27229     },
27230     getCoords : function (e) {
27231         var pt    = this.svgEl.dom.createSVGPoint();
27232         pt.x = e.clientX; 
27233         pt.y = e.clientY;
27234         if (this.isTouchEvent(e)) {
27235             pt.x =  e.targetTouches[0].clientX;
27236             pt.y = e.targetTouches[0].clientY;
27237         }
27238         var a = this.svgEl.dom.getScreenCTM();
27239         var b = a.inverse();
27240         var mx = pt.matrixTransform(b);
27241         return mx.x + ',' + mx.y;
27242     },
27243     //mouse event headler 
27244     down : function (e) {
27245         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27246         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27247         
27248         this.isMouseDown = true;
27249         
27250         e.preventDefault();
27251     },
27252     move : function (e) {
27253         if (this.isMouseDown) {
27254             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27255             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27256         }
27257         
27258         e.preventDefault();
27259     },
27260     up : function (e) {
27261         this.isMouseDown = false;
27262         var sp = this.signatureTmp.split(' ');
27263         
27264         if(sp.length > 1){
27265             if(!sp[sp.length-2].match(/^L/)){
27266                 sp.pop();
27267                 sp.pop();
27268                 sp.push("");
27269                 this.signatureTmp = sp.join(" ");
27270             }
27271         }
27272         if(this.getValue() != this.signatureTmp){
27273             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27274             this.isConfirmed = false;
27275         }
27276         e.preventDefault();
27277     },
27278     
27279     /**
27280      * Protected method that will not generally be called directly. It
27281      * is called when the editor creates its toolbar. Override this method if you need to
27282      * add custom toolbar buttons.
27283      * @param {HtmlEditor} editor
27284      */
27285     createToolbar : function(editor){
27286          function btn(id, toggle, handler){
27287             var xid = fid + '-'+ id ;
27288             return {
27289                 id : xid,
27290                 cmd : id,
27291                 cls : 'x-btn-icon x-edit-'+id,
27292                 enableToggle:toggle !== false,
27293                 scope: editor, // was editor...
27294                 handler:handler||editor.relayBtnCmd,
27295                 clickEvent:'mousedown',
27296                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27297                 tabIndex:-1
27298             };
27299         }
27300         
27301         
27302         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27303         this.tb = tb;
27304         this.tb.add(
27305            {
27306                 cls : ' x-signature-btn x-signature-'+id,
27307                 scope: editor, // was editor...
27308                 handler: this.reset,
27309                 clickEvent:'mousedown',
27310                 text: this.labels.clear
27311             },
27312             {
27313                  xtype : 'Fill',
27314                  xns: Roo.Toolbar
27315             }, 
27316             {
27317                 cls : '  x-signature-btn x-signature-'+id,
27318                 scope: editor, // was editor...
27319                 handler: this.confirmHandler,
27320                 clickEvent:'mousedown',
27321                 text: this.labels.confirm
27322             }
27323         );
27324     
27325     },
27326     //public
27327     /**
27328      * when user is clicked confirm then show this image.....
27329      * 
27330      * @return {String} Image Data URI
27331      */
27332     getImageDataURI : function(){
27333         var svg = this.svgEl.dom.parentNode.innerHTML;
27334         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
27335         return src; 
27336     },
27337     /**
27338      * 
27339      * @return {Boolean} this.isConfirmed
27340      */
27341     getConfirmed : function(){
27342         return this.isConfirmed;
27343     },
27344     /**
27345      * 
27346      * @return {Number} this.width
27347      */
27348     getWidth : function(){
27349         return this.width;
27350     },
27351     /**
27352      * 
27353      * @return {Number} this.height
27354      */
27355     getHeight : function(){
27356         return this.height;
27357     },
27358     // private
27359     getSignature : function(){
27360         return this.signatureTmp;
27361     },
27362     // private
27363     reset : function(){
27364         this.signatureTmp = '';
27365         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27366         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
27367         this.isConfirmed = false;
27368         Roo.form.Signature.superclass.reset.call(this);
27369     },
27370     setSignature : function(s){
27371         this.signatureTmp = s;
27372         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27373         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
27374         this.setValue(s);
27375         this.isConfirmed = false;
27376         Roo.form.Signature.superclass.reset.call(this);
27377     }, 
27378     test : function(){
27379 //        Roo.log(this.signPanel.dom.contentWindow.up())
27380     },
27381     //private
27382     setConfirmed : function(){
27383         
27384         
27385         
27386 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
27387     },
27388     // private
27389     confirmHandler : function(){
27390         if(!this.getSignature()){
27391             return;
27392         }
27393         
27394         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
27395         this.setValue(this.getSignature());
27396         this.isConfirmed = true;
27397         
27398         this.fireEvent('confirm', this);
27399     },
27400     // private
27401     // Subclasses should provide the validation implementation by overriding this
27402     validateValue : function(value){
27403         if(this.allowBlank){
27404             return true;
27405         }
27406         
27407         if(this.isConfirmed){
27408             return true;
27409         }
27410         return false;
27411     }
27412 });/*
27413  * Based on:
27414  * Ext JS Library 1.1.1
27415  * Copyright(c) 2006-2007, Ext JS, LLC.
27416  *
27417  * Originally Released Under LGPL - original licence link has changed is not relivant.
27418  *
27419  * Fork - LGPL
27420  * <script type="text/javascript">
27421  */
27422  
27423
27424 /**
27425  * @class Roo.form.ComboBox
27426  * @extends Roo.form.TriggerField
27427  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
27428  * @constructor
27429  * Create a new ComboBox.
27430  * @param {Object} config Configuration options
27431  */
27432 Roo.form.Select = function(config){
27433     Roo.form.Select.superclass.constructor.call(this, config);
27434      
27435 };
27436
27437 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
27438     /**
27439      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
27440      */
27441     /**
27442      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
27443      * rendering into an Roo.Editor, defaults to false)
27444      */
27445     /**
27446      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
27447      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
27448      */
27449     /**
27450      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
27451      */
27452     /**
27453      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
27454      * the dropdown list (defaults to undefined, with no header element)
27455      */
27456
27457      /**
27458      * @cfg {String/Roo.Template} tpl The template to use to render the output
27459      */
27460      
27461     // private
27462     defaultAutoCreate : {tag: "select"  },
27463     /**
27464      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
27465      */
27466     listWidth: undefined,
27467     /**
27468      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
27469      * mode = 'remote' or 'text' if mode = 'local')
27470      */
27471     displayField: undefined,
27472     /**
27473      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
27474      * mode = 'remote' or 'value' if mode = 'local'). 
27475      * Note: use of a valueField requires the user make a selection
27476      * in order for a value to be mapped.
27477      */
27478     valueField: undefined,
27479     
27480     
27481     /**
27482      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
27483      * field's data value (defaults to the underlying DOM element's name)
27484      */
27485     hiddenName: undefined,
27486     /**
27487      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
27488      */
27489     listClass: '',
27490     /**
27491      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
27492      */
27493     selectedClass: 'x-combo-selected',
27494     /**
27495      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
27496      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
27497      * which displays a downward arrow icon).
27498      */
27499     triggerClass : 'x-form-arrow-trigger',
27500     /**
27501      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
27502      */
27503     shadow:'sides',
27504     /**
27505      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
27506      * anchor positions (defaults to 'tl-bl')
27507      */
27508     listAlign: 'tl-bl?',
27509     /**
27510      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
27511      */
27512     maxHeight: 300,
27513     /**
27514      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
27515      * query specified by the allQuery config option (defaults to 'query')
27516      */
27517     triggerAction: 'query',
27518     /**
27519      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
27520      * (defaults to 4, does not apply if editable = false)
27521      */
27522     minChars : 4,
27523     /**
27524      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
27525      * delay (typeAheadDelay) if it matches a known value (defaults to false)
27526      */
27527     typeAhead: false,
27528     /**
27529      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
27530      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
27531      */
27532     queryDelay: 500,
27533     /**
27534      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
27535      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
27536      */
27537     pageSize: 0,
27538     /**
27539      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
27540      * when editable = true (defaults to false)
27541      */
27542     selectOnFocus:false,
27543     /**
27544      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
27545      */
27546     queryParam: 'query',
27547     /**
27548      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
27549      * when mode = 'remote' (defaults to 'Loading...')
27550      */
27551     loadingText: 'Loading...',
27552     /**
27553      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
27554      */
27555     resizable: false,
27556     /**
27557      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
27558      */
27559     handleHeight : 8,
27560     /**
27561      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
27562      * traditional select (defaults to true)
27563      */
27564     editable: true,
27565     /**
27566      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
27567      */
27568     allQuery: '',
27569     /**
27570      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
27571      */
27572     mode: 'remote',
27573     /**
27574      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
27575      * listWidth has a higher value)
27576      */
27577     minListWidth : 70,
27578     /**
27579      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
27580      * allow the user to set arbitrary text into the field (defaults to false)
27581      */
27582     forceSelection:false,
27583     /**
27584      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
27585      * if typeAhead = true (defaults to 250)
27586      */
27587     typeAheadDelay : 250,
27588     /**
27589      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
27590      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
27591      */
27592     valueNotFoundText : undefined,
27593     
27594     /**
27595      * @cfg {String} defaultValue The value displayed after loading the store.
27596      */
27597     defaultValue: '',
27598     
27599     /**
27600      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
27601      */
27602     blockFocus : false,
27603     
27604     /**
27605      * @cfg {Boolean} disableClear Disable showing of clear button.
27606      */
27607     disableClear : false,
27608     /**
27609      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
27610      */
27611     alwaysQuery : false,
27612     
27613     //private
27614     addicon : false,
27615     editicon: false,
27616     
27617     // element that contains real text value.. (when hidden is used..)
27618      
27619     // private
27620     onRender : function(ct, position){
27621         Roo.form.Field.prototype.onRender.call(this, ct, position);
27622         
27623         if(this.store){
27624             this.store.on('beforeload', this.onBeforeLoad, this);
27625             this.store.on('load', this.onLoad, this);
27626             this.store.on('loadexception', this.onLoadException, this);
27627             this.store.load({});
27628         }
27629         
27630         
27631         
27632     },
27633
27634     // private
27635     initEvents : function(){
27636         //Roo.form.ComboBox.superclass.initEvents.call(this);
27637  
27638     },
27639
27640     onDestroy : function(){
27641        
27642         if(this.store){
27643             this.store.un('beforeload', this.onBeforeLoad, this);
27644             this.store.un('load', this.onLoad, this);
27645             this.store.un('loadexception', this.onLoadException, this);
27646         }
27647         //Roo.form.ComboBox.superclass.onDestroy.call(this);
27648     },
27649
27650     // private
27651     fireKey : function(e){
27652         if(e.isNavKeyPress() && !this.list.isVisible()){
27653             this.fireEvent("specialkey", this, e);
27654         }
27655     },
27656
27657     // private
27658     onResize: function(w, h){
27659         
27660         return; 
27661     
27662         
27663     },
27664
27665     /**
27666      * Allow or prevent the user from directly editing the field text.  If false is passed,
27667      * the user will only be able to select from the items defined in the dropdown list.  This method
27668      * is the runtime equivalent of setting the 'editable' config option at config time.
27669      * @param {Boolean} value True to allow the user to directly edit the field text
27670      */
27671     setEditable : function(value){
27672          
27673     },
27674
27675     // private
27676     onBeforeLoad : function(){
27677         
27678         Roo.log("Select before load");
27679         return;
27680     
27681         this.innerList.update(this.loadingText ?
27682                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
27683         //this.restrictHeight();
27684         this.selectedIndex = -1;
27685     },
27686
27687     // private
27688     onLoad : function(){
27689
27690     
27691         var dom = this.el.dom;
27692         dom.innerHTML = '';
27693          var od = dom.ownerDocument;
27694          
27695         if (this.emptyText) {
27696             var op = od.createElement('option');
27697             op.setAttribute('value', '');
27698             op.innerHTML = String.format('{0}', this.emptyText);
27699             dom.appendChild(op);
27700         }
27701         if(this.store.getCount() > 0){
27702            
27703             var vf = this.valueField;
27704             var df = this.displayField;
27705             this.store.data.each(function(r) {
27706                 // which colmsn to use... testing - cdoe / title..
27707                 var op = od.createElement('option');
27708                 op.setAttribute('value', r.data[vf]);
27709                 op.innerHTML = String.format('{0}', r.data[df]);
27710                 dom.appendChild(op);
27711             });
27712             if (typeof(this.defaultValue != 'undefined')) {
27713                 this.setValue(this.defaultValue);
27714             }
27715             
27716              
27717         }else{
27718             //this.onEmptyResults();
27719         }
27720         //this.el.focus();
27721     },
27722     // private
27723     onLoadException : function()
27724     {
27725         dom.innerHTML = '';
27726             
27727         Roo.log("Select on load exception");
27728         return;
27729     
27730         this.collapse();
27731         Roo.log(this.store.reader.jsonData);
27732         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
27733             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
27734         }
27735         
27736         
27737     },
27738     // private
27739     onTypeAhead : function(){
27740          
27741     },
27742
27743     // private
27744     onSelect : function(record, index){
27745         Roo.log('on select?');
27746         return;
27747         if(this.fireEvent('beforeselect', this, record, index) !== false){
27748             this.setFromData(index > -1 ? record.data : false);
27749             this.collapse();
27750             this.fireEvent('select', this, record, index);
27751         }
27752     },
27753
27754     /**
27755      * Returns the currently selected field value or empty string if no value is set.
27756      * @return {String} value The selected value
27757      */
27758     getValue : function(){
27759         var dom = this.el.dom;
27760         this.value = dom.options[dom.selectedIndex].value;
27761         return this.value;
27762         
27763     },
27764
27765     /**
27766      * Clears any text/value currently set in the field
27767      */
27768     clearValue : function(){
27769         this.value = '';
27770         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
27771         
27772     },
27773
27774     /**
27775      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
27776      * will be displayed in the field.  If the value does not match the data value of an existing item,
27777      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
27778      * Otherwise the field will be blank (although the value will still be set).
27779      * @param {String} value The value to match
27780      */
27781     setValue : function(v){
27782         var d = this.el.dom;
27783         for (var i =0; i < d.options.length;i++) {
27784             if (v == d.options[i].value) {
27785                 d.selectedIndex = i;
27786                 this.value = v;
27787                 return;
27788             }
27789         }
27790         this.clearValue();
27791     },
27792     /**
27793      * @property {Object} the last set data for the element
27794      */
27795     
27796     lastData : false,
27797     /**
27798      * Sets the value of the field based on a object which is related to the record format for the store.
27799      * @param {Object} value the value to set as. or false on reset?
27800      */
27801     setFromData : function(o){
27802         Roo.log('setfrom data?');
27803          
27804         
27805         
27806     },
27807     // private
27808     reset : function(){
27809         this.clearValue();
27810     },
27811     // private
27812     findRecord : function(prop, value){
27813         
27814         return false;
27815     
27816         var record;
27817         if(this.store.getCount() > 0){
27818             this.store.each(function(r){
27819                 if(r.data[prop] == value){
27820                     record = r;
27821                     return false;
27822                 }
27823                 return true;
27824             });
27825         }
27826         return record;
27827     },
27828     
27829     getName: function()
27830     {
27831         // returns hidden if it's set..
27832         if (!this.rendered) {return ''};
27833         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
27834         
27835     },
27836      
27837
27838     
27839
27840     // private
27841     onEmptyResults : function(){
27842         Roo.log('empty results');
27843         //this.collapse();
27844     },
27845
27846     /**
27847      * Returns true if the dropdown list is expanded, else false.
27848      */
27849     isExpanded : function(){
27850         return false;
27851     },
27852
27853     /**
27854      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
27855      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
27856      * @param {String} value The data value of the item to select
27857      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
27858      * selected item if it is not currently in view (defaults to true)
27859      * @return {Boolean} True if the value matched an item in the list, else false
27860      */
27861     selectByValue : function(v, scrollIntoView){
27862         Roo.log('select By Value');
27863         return false;
27864     
27865         if(v !== undefined && v !== null){
27866             var r = this.findRecord(this.valueField || this.displayField, v);
27867             if(r){
27868                 this.select(this.store.indexOf(r), scrollIntoView);
27869                 return true;
27870             }
27871         }
27872         return false;
27873     },
27874
27875     /**
27876      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
27877      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
27878      * @param {Number} index The zero-based index of the list item to select
27879      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
27880      * selected item if it is not currently in view (defaults to true)
27881      */
27882     select : function(index, scrollIntoView){
27883         Roo.log('select ');
27884         return  ;
27885         
27886         this.selectedIndex = index;
27887         this.view.select(index);
27888         if(scrollIntoView !== false){
27889             var el = this.view.getNode(index);
27890             if(el){
27891                 this.innerList.scrollChildIntoView(el, false);
27892             }
27893         }
27894     },
27895
27896       
27897
27898     // private
27899     validateBlur : function(){
27900         
27901         return;
27902         
27903     },
27904
27905     // private
27906     initQuery : function(){
27907         this.doQuery(this.getRawValue());
27908     },
27909
27910     // private
27911     doForce : function(){
27912         if(this.el.dom.value.length > 0){
27913             this.el.dom.value =
27914                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
27915              
27916         }
27917     },
27918
27919     /**
27920      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
27921      * query allowing the query action to be canceled if needed.
27922      * @param {String} query The SQL query to execute
27923      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
27924      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
27925      * saved in the current store (defaults to false)
27926      */
27927     doQuery : function(q, forceAll){
27928         
27929         Roo.log('doQuery?');
27930         if(q === undefined || q === null){
27931             q = '';
27932         }
27933         var qe = {
27934             query: q,
27935             forceAll: forceAll,
27936             combo: this,
27937             cancel:false
27938         };
27939         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
27940             return false;
27941         }
27942         q = qe.query;
27943         forceAll = qe.forceAll;
27944         if(forceAll === true || (q.length >= this.minChars)){
27945             if(this.lastQuery != q || this.alwaysQuery){
27946                 this.lastQuery = q;
27947                 if(this.mode == 'local'){
27948                     this.selectedIndex = -1;
27949                     if(forceAll){
27950                         this.store.clearFilter();
27951                     }else{
27952                         this.store.filter(this.displayField, q);
27953                     }
27954                     this.onLoad();
27955                 }else{
27956                     this.store.baseParams[this.queryParam] = q;
27957                     this.store.load({
27958                         params: this.getParams(q)
27959                     });
27960                     this.expand();
27961                 }
27962             }else{
27963                 this.selectedIndex = -1;
27964                 this.onLoad();   
27965             }
27966         }
27967     },
27968
27969     // private
27970     getParams : function(q){
27971         var p = {};
27972         //p[this.queryParam] = q;
27973         if(this.pageSize){
27974             p.start = 0;
27975             p.limit = this.pageSize;
27976         }
27977         return p;
27978     },
27979
27980     /**
27981      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
27982      */
27983     collapse : function(){
27984         
27985     },
27986
27987     // private
27988     collapseIf : function(e){
27989         
27990     },
27991
27992     /**
27993      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
27994      */
27995     expand : function(){
27996         
27997     } ,
27998
27999     // private
28000      
28001
28002     /** 
28003     * @cfg {Boolean} grow 
28004     * @hide 
28005     */
28006     /** 
28007     * @cfg {Number} growMin 
28008     * @hide 
28009     */
28010     /** 
28011     * @cfg {Number} growMax 
28012     * @hide 
28013     */
28014     /**
28015      * @hide
28016      * @method autoSize
28017      */
28018     
28019     setWidth : function()
28020     {
28021         
28022     },
28023     getResizeEl : function(){
28024         return this.el;
28025     }
28026 });//<script type="text/javasscript">
28027  
28028
28029 /**
28030  * @class Roo.DDView
28031  * A DnD enabled version of Roo.View.
28032  * @param {Element/String} container The Element in which to create the View.
28033  * @param {String} tpl The template string used to create the markup for each element of the View
28034  * @param {Object} config The configuration properties. These include all the config options of
28035  * {@link Roo.View} plus some specific to this class.<br>
28036  * <p>
28037  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28038  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28039  * <p>
28040  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28041 .x-view-drag-insert-above {
28042         border-top:1px dotted #3366cc;
28043 }
28044 .x-view-drag-insert-below {
28045         border-bottom:1px dotted #3366cc;
28046 }
28047 </code></pre>
28048  * 
28049  */
28050  
28051 Roo.DDView = function(container, tpl, config) {
28052     Roo.DDView.superclass.constructor.apply(this, arguments);
28053     this.getEl().setStyle("outline", "0px none");
28054     this.getEl().unselectable();
28055     if (this.dragGroup) {
28056                 this.setDraggable(this.dragGroup.split(","));
28057     }
28058     if (this.dropGroup) {
28059                 this.setDroppable(this.dropGroup.split(","));
28060     }
28061     if (this.deletable) {
28062         this.setDeletable();
28063     }
28064     this.isDirtyFlag = false;
28065         this.addEvents({
28066                 "drop" : true
28067         });
28068 };
28069
28070 Roo.extend(Roo.DDView, Roo.View, {
28071 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28072 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28073 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28074 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28075
28076         isFormField: true,
28077
28078         reset: Roo.emptyFn,
28079         
28080         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28081
28082         validate: function() {
28083                 return true;
28084         },
28085         
28086         destroy: function() {
28087                 this.purgeListeners();
28088                 this.getEl.removeAllListeners();
28089                 this.getEl().remove();
28090                 if (this.dragZone) {
28091                         if (this.dragZone.destroy) {
28092                                 this.dragZone.destroy();
28093                         }
28094                 }
28095                 if (this.dropZone) {
28096                         if (this.dropZone.destroy) {
28097                                 this.dropZone.destroy();
28098                         }
28099                 }
28100         },
28101
28102 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28103         getName: function() {
28104                 return this.name;
28105         },
28106
28107 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28108         setValue: function(v) {
28109                 if (!this.store) {
28110                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28111                 }
28112                 var data = {};
28113                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28114                 this.store.proxy = new Roo.data.MemoryProxy(data);
28115                 this.store.load();
28116         },
28117
28118 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28119         getValue: function() {
28120                 var result = '(';
28121                 this.store.each(function(rec) {
28122                         result += rec.id + ',';
28123                 });
28124                 return result.substr(0, result.length - 1) + ')';
28125         },
28126         
28127         getIds: function() {
28128                 var i = 0, result = new Array(this.store.getCount());
28129                 this.store.each(function(rec) {
28130                         result[i++] = rec.id;
28131                 });
28132                 return result;
28133         },
28134         
28135         isDirty: function() {
28136                 return this.isDirtyFlag;
28137         },
28138
28139 /**
28140  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28141  *      whole Element becomes the target, and this causes the drop gesture to append.
28142  */
28143     getTargetFromEvent : function(e) {
28144                 var target = e.getTarget();
28145                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28146                 target = target.parentNode;
28147                 }
28148                 if (!target) {
28149                         target = this.el.dom.lastChild || this.el.dom;
28150                 }
28151                 return target;
28152     },
28153
28154 /**
28155  *      Create the drag data which consists of an object which has the property "ddel" as
28156  *      the drag proxy element. 
28157  */
28158     getDragData : function(e) {
28159         var target = this.findItemFromChild(e.getTarget());
28160                 if(target) {
28161                         this.handleSelection(e);
28162                         var selNodes = this.getSelectedNodes();
28163             var dragData = {
28164                 source: this,
28165                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28166                 nodes: selNodes,
28167                 records: []
28168                         };
28169                         var selectedIndices = this.getSelectedIndexes();
28170                         for (var i = 0; i < selectedIndices.length; i++) {
28171                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28172                         }
28173                         if (selNodes.length == 1) {
28174                                 dragData.ddel = target.cloneNode(true); // the div element
28175                         } else {
28176                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28177                                 div.className = 'multi-proxy';
28178                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28179                                         div.appendChild(selNodes[i].cloneNode(true));
28180                                 }
28181                                 dragData.ddel = div;
28182                         }
28183             //console.log(dragData)
28184             //console.log(dragData.ddel.innerHTML)
28185                         return dragData;
28186                 }
28187         //console.log('nodragData')
28188                 return false;
28189     },
28190     
28191 /**     Specify to which ddGroup items in this DDView may be dragged. */
28192     setDraggable: function(ddGroup) {
28193         if (ddGroup instanceof Array) {
28194                 Roo.each(ddGroup, this.setDraggable, this);
28195                 return;
28196         }
28197         if (this.dragZone) {
28198                 this.dragZone.addToGroup(ddGroup);
28199         } else {
28200                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28201                                 containerScroll: true,
28202                                 ddGroup: ddGroup 
28203
28204                         });
28205 //                      Draggability implies selection. DragZone's mousedown selects the element.
28206                         if (!this.multiSelect) { this.singleSelect = true; }
28207
28208 //                      Wire the DragZone's handlers up to methods in *this*
28209                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28210                 }
28211     },
28212
28213 /**     Specify from which ddGroup this DDView accepts drops. */
28214     setDroppable: function(ddGroup) {
28215         if (ddGroup instanceof Array) {
28216                 Roo.each(ddGroup, this.setDroppable, this);
28217                 return;
28218         }
28219         if (this.dropZone) {
28220                 this.dropZone.addToGroup(ddGroup);
28221         } else {
28222                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28223                                 containerScroll: true,
28224                                 ddGroup: ddGroup
28225                         });
28226
28227 //                      Wire the DropZone's handlers up to methods in *this*
28228                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28229                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28230                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28231                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28232                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28233                 }
28234     },
28235
28236 /**     Decide whether to drop above or below a View node. */
28237     getDropPoint : function(e, n, dd){
28238         if (n == this.el.dom) { return "above"; }
28239                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28240                 var c = t + (b - t) / 2;
28241                 var y = Roo.lib.Event.getPageY(e);
28242                 if(y <= c) {
28243                         return "above";
28244                 }else{
28245                         return "below";
28246                 }
28247     },
28248
28249     onNodeEnter : function(n, dd, e, data){
28250                 return false;
28251     },
28252     
28253     onNodeOver : function(n, dd, e, data){
28254                 var pt = this.getDropPoint(e, n, dd);
28255                 // set the insert point style on the target node
28256                 var dragElClass = this.dropNotAllowed;
28257                 if (pt) {
28258                         var targetElClass;
28259                         if (pt == "above"){
28260                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28261                                 targetElClass = "x-view-drag-insert-above";
28262                         } else {
28263                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28264                                 targetElClass = "x-view-drag-insert-below";
28265                         }
28266                         if (this.lastInsertClass != targetElClass){
28267                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28268                                 this.lastInsertClass = targetElClass;
28269                         }
28270                 }
28271                 return dragElClass;
28272         },
28273
28274     onNodeOut : function(n, dd, e, data){
28275                 this.removeDropIndicators(n);
28276     },
28277
28278     onNodeDrop : function(n, dd, e, data){
28279         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28280                 return false;
28281         }
28282         var pt = this.getDropPoint(e, n, dd);
28283                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28284                 if (pt == "below") { insertAt++; }
28285                 for (var i = 0; i < data.records.length; i++) {
28286                         var r = data.records[i];
28287                         var dup = this.store.getById(r.id);
28288                         if (dup && (dd != this.dragZone)) {
28289                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28290                         } else {
28291                                 if (data.copy) {
28292                                         this.store.insert(insertAt++, r.copy());
28293                                 } else {
28294                                         data.source.isDirtyFlag = true;
28295                                         r.store.remove(r);
28296                                         this.store.insert(insertAt++, r);
28297                                 }
28298                                 this.isDirtyFlag = true;
28299                         }
28300                 }
28301                 this.dragZone.cachedTarget = null;
28302                 return true;
28303     },
28304
28305     removeDropIndicators : function(n){
28306                 if(n){
28307                         Roo.fly(n).removeClass([
28308                                 "x-view-drag-insert-above",
28309                                 "x-view-drag-insert-below"]);
28310                         this.lastInsertClass = "_noclass";
28311                 }
28312     },
28313
28314 /**
28315  *      Utility method. Add a delete option to the DDView's context menu.
28316  *      @param {String} imageUrl The URL of the "delete" icon image.
28317  */
28318         setDeletable: function(imageUrl) {
28319                 if (!this.singleSelect && !this.multiSelect) {
28320                         this.singleSelect = true;
28321                 }
28322                 var c = this.getContextMenu();
28323                 this.contextMenu.on("itemclick", function(item) {
28324                         switch (item.id) {
28325                                 case "delete":
28326                                         this.remove(this.getSelectedIndexes());
28327                                         break;
28328                         }
28329                 }, this);
28330                 this.contextMenu.add({
28331                         icon: imageUrl,
28332                         id: "delete",
28333                         text: 'Delete'
28334                 });
28335         },
28336         
28337 /**     Return the context menu for this DDView. */
28338         getContextMenu: function() {
28339                 if (!this.contextMenu) {
28340 //                      Create the View's context menu
28341                         this.contextMenu = new Roo.menu.Menu({
28342                                 id: this.id + "-contextmenu"
28343                         });
28344                         this.el.on("contextmenu", this.showContextMenu, this);
28345                 }
28346                 return this.contextMenu;
28347         },
28348         
28349         disableContextMenu: function() {
28350                 if (this.contextMenu) {
28351                         this.el.un("contextmenu", this.showContextMenu, this);
28352                 }
28353         },
28354
28355         showContextMenu: function(e, item) {
28356         item = this.findItemFromChild(e.getTarget());
28357                 if (item) {
28358                         e.stopEvent();
28359                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28360                         this.contextMenu.showAt(e.getXY());
28361             }
28362     },
28363
28364 /**
28365  *      Remove {@link Roo.data.Record}s at the specified indices.
28366  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28367  */
28368     remove: function(selectedIndices) {
28369                 selectedIndices = [].concat(selectedIndices);
28370                 for (var i = 0; i < selectedIndices.length; i++) {
28371                         var rec = this.store.getAt(selectedIndices[i]);
28372                         this.store.remove(rec);
28373                 }
28374     },
28375
28376 /**
28377  *      Double click fires the event, but also, if this is draggable, and there is only one other
28378  *      related DropZone, it transfers the selected node.
28379  */
28380     onDblClick : function(e){
28381         var item = this.findItemFromChild(e.getTarget());
28382         if(item){
28383             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28384                 return false;
28385             }
28386             if (this.dragGroup) {
28387                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28388                     while (targets.indexOf(this.dropZone) > -1) {
28389                             targets.remove(this.dropZone);
28390                                 }
28391                     if (targets.length == 1) {
28392                                         this.dragZone.cachedTarget = null;
28393                         var el = Roo.get(targets[0].getEl());
28394                         var box = el.getBox(true);
28395                         targets[0].onNodeDrop(el.dom, {
28396                                 target: el.dom,
28397                                 xy: [box.x, box.y + box.height - 1]
28398                         }, null, this.getDragData(e));
28399                     }
28400                 }
28401         }
28402     },
28403     
28404     handleSelection: function(e) {
28405                 this.dragZone.cachedTarget = null;
28406         var item = this.findItemFromChild(e.getTarget());
28407         if (!item) {
28408                 this.clearSelections(true);
28409                 return;
28410         }
28411                 if (item && (this.multiSelect || this.singleSelect)){
28412                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28413                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28414                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28415                                 this.unselect(item);
28416                         } else {
28417                                 this.select(item, this.multiSelect && e.ctrlKey);
28418                                 this.lastSelection = item;
28419                         }
28420                 }
28421     },
28422
28423     onItemClick : function(item, index, e){
28424                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28425                         return false;
28426                 }
28427                 return true;
28428     },
28429
28430     unselect : function(nodeInfo, suppressEvent){
28431                 var node = this.getNode(nodeInfo);
28432                 if(node && this.isSelected(node)){
28433                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28434                                 Roo.fly(node).removeClass(this.selectedClass);
28435                                 this.selections.remove(node);
28436                                 if(!suppressEvent){
28437                                         this.fireEvent("selectionchange", this, this.selections);
28438                                 }
28439                         }
28440                 }
28441     }
28442 });
28443 /*
28444  * Based on:
28445  * Ext JS Library 1.1.1
28446  * Copyright(c) 2006-2007, Ext JS, LLC.
28447  *
28448  * Originally Released Under LGPL - original licence link has changed is not relivant.
28449  *
28450  * Fork - LGPL
28451  * <script type="text/javascript">
28452  */
28453  
28454 /**
28455  * @class Roo.LayoutManager
28456  * @extends Roo.util.Observable
28457  * Base class for layout managers.
28458  */
28459 Roo.LayoutManager = function(container, config){
28460     Roo.LayoutManager.superclass.constructor.call(this);
28461     this.el = Roo.get(container);
28462     // ie scrollbar fix
28463     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28464         document.body.scroll = "no";
28465     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28466         this.el.position('relative');
28467     }
28468     this.id = this.el.id;
28469     this.el.addClass("x-layout-container");
28470     /** false to disable window resize monitoring @type Boolean */
28471     this.monitorWindowResize = true;
28472     this.regions = {};
28473     this.addEvents({
28474         /**
28475          * @event layout
28476          * Fires when a layout is performed. 
28477          * @param {Roo.LayoutManager} this
28478          */
28479         "layout" : true,
28480         /**
28481          * @event regionresized
28482          * Fires when the user resizes a region. 
28483          * @param {Roo.LayoutRegion} region The resized region
28484          * @param {Number} newSize The new size (width for east/west, height for north/south)
28485          */
28486         "regionresized" : true,
28487         /**
28488          * @event regioncollapsed
28489          * Fires when a region is collapsed. 
28490          * @param {Roo.LayoutRegion} region The collapsed region
28491          */
28492         "regioncollapsed" : true,
28493         /**
28494          * @event regionexpanded
28495          * Fires when a region is expanded.  
28496          * @param {Roo.LayoutRegion} region The expanded region
28497          */
28498         "regionexpanded" : true
28499     });
28500     this.updating = false;
28501     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28502 };
28503
28504 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28505     /**
28506      * Returns true if this layout is currently being updated
28507      * @return {Boolean}
28508      */
28509     isUpdating : function(){
28510         return this.updating; 
28511     },
28512     
28513     /**
28514      * Suspend the LayoutManager from doing auto-layouts while
28515      * making multiple add or remove calls
28516      */
28517     beginUpdate : function(){
28518         this.updating = true;    
28519     },
28520     
28521     /**
28522      * Restore auto-layouts and optionally disable the manager from performing a layout
28523      * @param {Boolean} noLayout true to disable a layout update 
28524      */
28525     endUpdate : function(noLayout){
28526         this.updating = false;
28527         if(!noLayout){
28528             this.layout();
28529         }    
28530     },
28531     
28532     layout: function(){
28533         
28534     },
28535     
28536     onRegionResized : function(region, newSize){
28537         this.fireEvent("regionresized", region, newSize);
28538         this.layout();
28539     },
28540     
28541     onRegionCollapsed : function(region){
28542         this.fireEvent("regioncollapsed", region);
28543     },
28544     
28545     onRegionExpanded : function(region){
28546         this.fireEvent("regionexpanded", region);
28547     },
28548         
28549     /**
28550      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28551      * performs box-model adjustments.
28552      * @return {Object} The size as an object {width: (the width), height: (the height)}
28553      */
28554     getViewSize : function(){
28555         var size;
28556         if(this.el.dom != document.body){
28557             size = this.el.getSize();
28558         }else{
28559             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28560         }
28561         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28562         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28563         return size;
28564     },
28565     
28566     /**
28567      * Returns the Element this layout is bound to.
28568      * @return {Roo.Element}
28569      */
28570     getEl : function(){
28571         return this.el;
28572     },
28573     
28574     /**
28575      * Returns the specified region.
28576      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
28577      * @return {Roo.LayoutRegion}
28578      */
28579     getRegion : function(target){
28580         return this.regions[target.toLowerCase()];
28581     },
28582     
28583     onWindowResize : function(){
28584         if(this.monitorWindowResize){
28585             this.layout();
28586         }
28587     }
28588 });/*
28589  * Based on:
28590  * Ext JS Library 1.1.1
28591  * Copyright(c) 2006-2007, Ext JS, LLC.
28592  *
28593  * Originally Released Under LGPL - original licence link has changed is not relivant.
28594  *
28595  * Fork - LGPL
28596  * <script type="text/javascript">
28597  */
28598 /**
28599  * @class Roo.BorderLayout
28600  * @extends Roo.LayoutManager
28601  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
28602  * please see: <br><br>
28603  * <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>
28604  * <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>
28605  * Example:
28606  <pre><code>
28607  var layout = new Roo.BorderLayout(document.body, {
28608     north: {
28609         initialSize: 25,
28610         titlebar: false
28611     },
28612     west: {
28613         split:true,
28614         initialSize: 200,
28615         minSize: 175,
28616         maxSize: 400,
28617         titlebar: true,
28618         collapsible: true
28619     },
28620     east: {
28621         split:true,
28622         initialSize: 202,
28623         minSize: 175,
28624         maxSize: 400,
28625         titlebar: true,
28626         collapsible: true
28627     },
28628     south: {
28629         split:true,
28630         initialSize: 100,
28631         minSize: 100,
28632         maxSize: 200,
28633         titlebar: true,
28634         collapsible: true
28635     },
28636     center: {
28637         titlebar: true,
28638         autoScroll:true,
28639         resizeTabs: true,
28640         minTabWidth: 50,
28641         preferredTabWidth: 150
28642     }
28643 });
28644
28645 // shorthand
28646 var CP = Roo.ContentPanel;
28647
28648 layout.beginUpdate();
28649 layout.add("north", new CP("north", "North"));
28650 layout.add("south", new CP("south", {title: "South", closable: true}));
28651 layout.add("west", new CP("west", {title: "West"}));
28652 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
28653 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
28654 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
28655 layout.getRegion("center").showPanel("center1");
28656 layout.endUpdate();
28657 </code></pre>
28658
28659 <b>The container the layout is rendered into can be either the body element or any other element.
28660 If it is not the body element, the container needs to either be an absolute positioned element,
28661 or you will need to add "position:relative" to the css of the container.  You will also need to specify
28662 the container size if it is not the body element.</b>
28663
28664 * @constructor
28665 * Create a new BorderLayout
28666 * @param {String/HTMLElement/Element} container The container this layout is bound to
28667 * @param {Object} config Configuration options
28668  */
28669 Roo.BorderLayout = function(container, config){
28670     config = config || {};
28671     Roo.BorderLayout.superclass.constructor.call(this, container, config);
28672     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
28673     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
28674         var target = this.factory.validRegions[i];
28675         if(config[target]){
28676             this.addRegion(target, config[target]);
28677         }
28678     }
28679 };
28680
28681 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
28682     /**
28683      * Creates and adds a new region if it doesn't already exist.
28684      * @param {String} target The target region key (north, south, east, west or center).
28685      * @param {Object} config The regions config object
28686      * @return {BorderLayoutRegion} The new region
28687      */
28688     addRegion : function(target, config){
28689         if(!this.regions[target]){
28690             var r = this.factory.create(target, this, config);
28691             this.bindRegion(target, r);
28692         }
28693         return this.regions[target];
28694     },
28695
28696     // private (kinda)
28697     bindRegion : function(name, r){
28698         this.regions[name] = r;
28699         r.on("visibilitychange", this.layout, this);
28700         r.on("paneladded", this.layout, this);
28701         r.on("panelremoved", this.layout, this);
28702         r.on("invalidated", this.layout, this);
28703         r.on("resized", this.onRegionResized, this);
28704         r.on("collapsed", this.onRegionCollapsed, this);
28705         r.on("expanded", this.onRegionExpanded, this);
28706     },
28707
28708     /**
28709      * Performs a layout update.
28710      */
28711     layout : function(){
28712         if(this.updating) {
28713             return;
28714         }
28715         var size = this.getViewSize();
28716         var w = size.width;
28717         var h = size.height;
28718         var centerW = w;
28719         var centerH = h;
28720         var centerY = 0;
28721         var centerX = 0;
28722         //var x = 0, y = 0;
28723
28724         var rs = this.regions;
28725         var north = rs["north"];
28726         var south = rs["south"]; 
28727         var west = rs["west"];
28728         var east = rs["east"];
28729         var center = rs["center"];
28730         //if(this.hideOnLayout){ // not supported anymore
28731             //c.el.setStyle("display", "none");
28732         //}
28733         if(north && north.isVisible()){
28734             var b = north.getBox();
28735             var m = north.getMargins();
28736             b.width = w - (m.left+m.right);
28737             b.x = m.left;
28738             b.y = m.top;
28739             centerY = b.height + b.y + m.bottom;
28740             centerH -= centerY;
28741             north.updateBox(this.safeBox(b));
28742         }
28743         if(south && south.isVisible()){
28744             var b = south.getBox();
28745             var m = south.getMargins();
28746             b.width = w - (m.left+m.right);
28747             b.x = m.left;
28748             var totalHeight = (b.height + m.top + m.bottom);
28749             b.y = h - totalHeight + m.top;
28750             centerH -= totalHeight;
28751             south.updateBox(this.safeBox(b));
28752         }
28753         if(west && west.isVisible()){
28754             var b = west.getBox();
28755             var m = west.getMargins();
28756             b.height = centerH - (m.top+m.bottom);
28757             b.x = m.left;
28758             b.y = centerY + m.top;
28759             var totalWidth = (b.width + m.left + m.right);
28760             centerX += totalWidth;
28761             centerW -= totalWidth;
28762             west.updateBox(this.safeBox(b));
28763         }
28764         if(east && east.isVisible()){
28765             var b = east.getBox();
28766             var m = east.getMargins();
28767             b.height = centerH - (m.top+m.bottom);
28768             var totalWidth = (b.width + m.left + m.right);
28769             b.x = w - totalWidth + m.left;
28770             b.y = centerY + m.top;
28771             centerW -= totalWidth;
28772             east.updateBox(this.safeBox(b));
28773         }
28774         if(center){
28775             var m = center.getMargins();
28776             var centerBox = {
28777                 x: centerX + m.left,
28778                 y: centerY + m.top,
28779                 width: centerW - (m.left+m.right),
28780                 height: centerH - (m.top+m.bottom)
28781             };
28782             //if(this.hideOnLayout){
28783                 //center.el.setStyle("display", "block");
28784             //}
28785             center.updateBox(this.safeBox(centerBox));
28786         }
28787         this.el.repaint();
28788         this.fireEvent("layout", this);
28789     },
28790
28791     // private
28792     safeBox : function(box){
28793         box.width = Math.max(0, box.width);
28794         box.height = Math.max(0, box.height);
28795         return box;
28796     },
28797
28798     /**
28799      * Adds a ContentPanel (or subclass) to this layout.
28800      * @param {String} target The target region key (north, south, east, west or center).
28801      * @param {Roo.ContentPanel} panel The panel to add
28802      * @return {Roo.ContentPanel} The added panel
28803      */
28804     add : function(target, panel){
28805          
28806         target = target.toLowerCase();
28807         return this.regions[target].add(panel);
28808     },
28809
28810     /**
28811      * Remove a ContentPanel (or subclass) to this layout.
28812      * @param {String} target The target region key (north, south, east, west or center).
28813      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
28814      * @return {Roo.ContentPanel} The removed panel
28815      */
28816     remove : function(target, panel){
28817         target = target.toLowerCase();
28818         return this.regions[target].remove(panel);
28819     },
28820
28821     /**
28822      * Searches all regions for a panel with the specified id
28823      * @param {String} panelId
28824      * @return {Roo.ContentPanel} The panel or null if it wasn't found
28825      */
28826     findPanel : function(panelId){
28827         var rs = this.regions;
28828         for(var target in rs){
28829             if(typeof rs[target] != "function"){
28830                 var p = rs[target].getPanel(panelId);
28831                 if(p){
28832                     return p;
28833                 }
28834             }
28835         }
28836         return null;
28837     },
28838
28839     /**
28840      * Searches all regions for a panel with the specified id and activates (shows) it.
28841      * @param {String/ContentPanel} panelId The panels id or the panel itself
28842      * @return {Roo.ContentPanel} The shown panel or null
28843      */
28844     showPanel : function(panelId) {
28845       var rs = this.regions;
28846       for(var target in rs){
28847          var r = rs[target];
28848          if(typeof r != "function"){
28849             if(r.hasPanel(panelId)){
28850                return r.showPanel(panelId);
28851             }
28852          }
28853       }
28854       return null;
28855    },
28856
28857    /**
28858      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
28859      * @param {Roo.state.Provider} provider (optional) An alternate state provider
28860      */
28861     restoreState : function(provider){
28862         if(!provider){
28863             provider = Roo.state.Manager;
28864         }
28865         var sm = new Roo.LayoutStateManager();
28866         sm.init(this, provider);
28867     },
28868
28869     /**
28870      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
28871      * object should contain properties for each region to add ContentPanels to, and each property's value should be
28872      * a valid ContentPanel config object.  Example:
28873      * <pre><code>
28874 // Create the main layout
28875 var layout = new Roo.BorderLayout('main-ct', {
28876     west: {
28877         split:true,
28878         minSize: 175,
28879         titlebar: true
28880     },
28881     center: {
28882         title:'Components'
28883     }
28884 }, 'main-ct');
28885
28886 // Create and add multiple ContentPanels at once via configs
28887 layout.batchAdd({
28888    west: {
28889        id: 'source-files',
28890        autoCreate:true,
28891        title:'Ext Source Files',
28892        autoScroll:true,
28893        fitToFrame:true
28894    },
28895    center : {
28896        el: cview,
28897        autoScroll:true,
28898        fitToFrame:true,
28899        toolbar: tb,
28900        resizeEl:'cbody'
28901    }
28902 });
28903 </code></pre>
28904      * @param {Object} regions An object containing ContentPanel configs by region name
28905      */
28906     batchAdd : function(regions){
28907         this.beginUpdate();
28908         for(var rname in regions){
28909             var lr = this.regions[rname];
28910             if(lr){
28911                 this.addTypedPanels(lr, regions[rname]);
28912             }
28913         }
28914         this.endUpdate();
28915     },
28916
28917     // private
28918     addTypedPanels : function(lr, ps){
28919         if(typeof ps == 'string'){
28920             lr.add(new Roo.ContentPanel(ps));
28921         }
28922         else if(ps instanceof Array){
28923             for(var i =0, len = ps.length; i < len; i++){
28924                 this.addTypedPanels(lr, ps[i]);
28925             }
28926         }
28927         else if(!ps.events){ // raw config?
28928             var el = ps.el;
28929             delete ps.el; // prevent conflict
28930             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
28931         }
28932         else {  // panel object assumed!
28933             lr.add(ps);
28934         }
28935     },
28936     /**
28937      * Adds a xtype elements to the layout.
28938      * <pre><code>
28939
28940 layout.addxtype({
28941        xtype : 'ContentPanel',
28942        region: 'west',
28943        items: [ .... ]
28944    }
28945 );
28946
28947 layout.addxtype({
28948         xtype : 'NestedLayoutPanel',
28949         region: 'west',
28950         layout: {
28951            center: { },
28952            west: { }   
28953         },
28954         items : [ ... list of content panels or nested layout panels.. ]
28955    }
28956 );
28957 </code></pre>
28958      * @param {Object} cfg Xtype definition of item to add.
28959      */
28960     addxtype : function(cfg)
28961     {
28962         // basically accepts a pannel...
28963         // can accept a layout region..!?!?
28964         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
28965         
28966         if (!cfg.xtype.match(/Panel$/)) {
28967             return false;
28968         }
28969         var ret = false;
28970         
28971         if (typeof(cfg.region) == 'undefined') {
28972             Roo.log("Failed to add Panel, region was not set");
28973             Roo.log(cfg);
28974             return false;
28975         }
28976         var region = cfg.region;
28977         delete cfg.region;
28978         
28979           
28980         var xitems = [];
28981         if (cfg.items) {
28982             xitems = cfg.items;
28983             delete cfg.items;
28984         }
28985         var nb = false;
28986         
28987         switch(cfg.xtype) 
28988         {
28989             case 'ContentPanel':  // ContentPanel (el, cfg)
28990             case 'ScrollPanel':  // ContentPanel (el, cfg)
28991             case 'ViewPanel': 
28992                 if(cfg.autoCreate) {
28993                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
28994                 } else {
28995                     var el = this.el.createChild();
28996                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
28997                 }
28998                 
28999                 this.add(region, ret);
29000                 break;
29001             
29002             
29003             case 'TreePanel': // our new panel!
29004                 cfg.el = this.el.createChild();
29005                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29006                 this.add(region, ret);
29007                 break;
29008             
29009             case 'NestedLayoutPanel': 
29010                 // create a new Layout (which is  a Border Layout...
29011                 var el = this.el.createChild();
29012                 var clayout = cfg.layout;
29013                 delete cfg.layout;
29014                 clayout.items   = clayout.items  || [];
29015                 // replace this exitems with the clayout ones..
29016                 xitems = clayout.items;
29017                  
29018                 
29019                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29020                     cfg.background = false;
29021                 }
29022                 var layout = new Roo.BorderLayout(el, clayout);
29023                 
29024                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29025                 //console.log('adding nested layout panel '  + cfg.toSource());
29026                 this.add(region, ret);
29027                 nb = {}; /// find first...
29028                 break;
29029                 
29030             case 'GridPanel': 
29031             
29032                 // needs grid and region
29033                 
29034                 //var el = this.getRegion(region).el.createChild();
29035                 var el = this.el.createChild();
29036                 // create the grid first...
29037                 
29038                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29039                 delete cfg.grid;
29040                 if (region == 'center' && this.active ) {
29041                     cfg.background = false;
29042                 }
29043                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29044                 
29045                 this.add(region, ret);
29046                 if (cfg.background) {
29047                     ret.on('activate', function(gp) {
29048                         if (!gp.grid.rendered) {
29049                             gp.grid.render();
29050                         }
29051                     });
29052                 } else {
29053                     grid.render();
29054                 }
29055                 break;
29056            
29057            
29058            
29059                 
29060                 
29061                 
29062             default:
29063                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29064                     
29065                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29066                     this.add(region, ret);
29067                 } else {
29068                 
29069                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29070                     return null;
29071                 }
29072                 
29073              // GridPanel (grid, cfg)
29074             
29075         }
29076         this.beginUpdate();
29077         // add children..
29078         var region = '';
29079         var abn = {};
29080         Roo.each(xitems, function(i)  {
29081             region = nb && i.region ? i.region : false;
29082             
29083             var add = ret.addxtype(i);
29084            
29085             if (region) {
29086                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29087                 if (!i.background) {
29088                     abn[region] = nb[region] ;
29089                 }
29090             }
29091             
29092         });
29093         this.endUpdate();
29094
29095         // make the last non-background panel active..
29096         //if (nb) { Roo.log(abn); }
29097         if (nb) {
29098             
29099             for(var r in abn) {
29100                 region = this.getRegion(r);
29101                 if (region) {
29102                     // tried using nb[r], but it does not work..
29103                      
29104                     region.showPanel(abn[r]);
29105                    
29106                 }
29107             }
29108         }
29109         return ret;
29110         
29111     }
29112 });
29113
29114 /**
29115  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29116  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29117  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29118  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29119  * <pre><code>
29120 // shorthand
29121 var CP = Roo.ContentPanel;
29122
29123 var layout = Roo.BorderLayout.create({
29124     north: {
29125         initialSize: 25,
29126         titlebar: false,
29127         panels: [new CP("north", "North")]
29128     },
29129     west: {
29130         split:true,
29131         initialSize: 200,
29132         minSize: 175,
29133         maxSize: 400,
29134         titlebar: true,
29135         collapsible: true,
29136         panels: [new CP("west", {title: "West"})]
29137     },
29138     east: {
29139         split:true,
29140         initialSize: 202,
29141         minSize: 175,
29142         maxSize: 400,
29143         titlebar: true,
29144         collapsible: true,
29145         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29146     },
29147     south: {
29148         split:true,
29149         initialSize: 100,
29150         minSize: 100,
29151         maxSize: 200,
29152         titlebar: true,
29153         collapsible: true,
29154         panels: [new CP("south", {title: "South", closable: true})]
29155     },
29156     center: {
29157         titlebar: true,
29158         autoScroll:true,
29159         resizeTabs: true,
29160         minTabWidth: 50,
29161         preferredTabWidth: 150,
29162         panels: [
29163             new CP("center1", {title: "Close Me", closable: true}),
29164             new CP("center2", {title: "Center Panel", closable: false})
29165         ]
29166     }
29167 }, document.body);
29168
29169 layout.getRegion("center").showPanel("center1");
29170 </code></pre>
29171  * @param config
29172  * @param targetEl
29173  */
29174 Roo.BorderLayout.create = function(config, targetEl){
29175     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29176     layout.beginUpdate();
29177     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29178     for(var j = 0, jlen = regions.length; j < jlen; j++){
29179         var lr = regions[j];
29180         if(layout.regions[lr] && config[lr].panels){
29181             var r = layout.regions[lr];
29182             var ps = config[lr].panels;
29183             layout.addTypedPanels(r, ps);
29184         }
29185     }
29186     layout.endUpdate();
29187     return layout;
29188 };
29189
29190 // private
29191 Roo.BorderLayout.RegionFactory = {
29192     // private
29193     validRegions : ["north","south","east","west","center"],
29194
29195     // private
29196     create : function(target, mgr, config){
29197         target = target.toLowerCase();
29198         if(config.lightweight || config.basic){
29199             return new Roo.BasicLayoutRegion(mgr, config, target);
29200         }
29201         switch(target){
29202             case "north":
29203                 return new Roo.NorthLayoutRegion(mgr, config);
29204             case "south":
29205                 return new Roo.SouthLayoutRegion(mgr, config);
29206             case "east":
29207                 return new Roo.EastLayoutRegion(mgr, config);
29208             case "west":
29209                 return new Roo.WestLayoutRegion(mgr, config);
29210             case "center":
29211                 return new Roo.CenterLayoutRegion(mgr, config);
29212         }
29213         throw 'Layout region "'+target+'" not supported.';
29214     }
29215 };/*
29216  * Based on:
29217  * Ext JS Library 1.1.1
29218  * Copyright(c) 2006-2007, Ext JS, LLC.
29219  *
29220  * Originally Released Under LGPL - original licence link has changed is not relivant.
29221  *
29222  * Fork - LGPL
29223  * <script type="text/javascript">
29224  */
29225  
29226 /**
29227  * @class Roo.BasicLayoutRegion
29228  * @extends Roo.util.Observable
29229  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29230  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29231  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29232  */
29233 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29234     this.mgr = mgr;
29235     this.position  = pos;
29236     this.events = {
29237         /**
29238          * @scope Roo.BasicLayoutRegion
29239          */
29240         
29241         /**
29242          * @event beforeremove
29243          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29244          * @param {Roo.LayoutRegion} this
29245          * @param {Roo.ContentPanel} panel The panel
29246          * @param {Object} e The cancel event object
29247          */
29248         "beforeremove" : true,
29249         /**
29250          * @event invalidated
29251          * Fires when the layout for this region is changed.
29252          * @param {Roo.LayoutRegion} this
29253          */
29254         "invalidated" : true,
29255         /**
29256          * @event visibilitychange
29257          * Fires when this region is shown or hidden 
29258          * @param {Roo.LayoutRegion} this
29259          * @param {Boolean} visibility true or false
29260          */
29261         "visibilitychange" : true,
29262         /**
29263          * @event paneladded
29264          * Fires when a panel is added. 
29265          * @param {Roo.LayoutRegion} this
29266          * @param {Roo.ContentPanel} panel The panel
29267          */
29268         "paneladded" : true,
29269         /**
29270          * @event panelremoved
29271          * Fires when a panel is removed. 
29272          * @param {Roo.LayoutRegion} this
29273          * @param {Roo.ContentPanel} panel The panel
29274          */
29275         "panelremoved" : true,
29276         /**
29277          * @event beforecollapse
29278          * Fires when this region before collapse.
29279          * @param {Roo.LayoutRegion} this
29280          */
29281         "beforecollapse" : true,
29282         /**
29283          * @event collapsed
29284          * Fires when this region is collapsed.
29285          * @param {Roo.LayoutRegion} this
29286          */
29287         "collapsed" : true,
29288         /**
29289          * @event expanded
29290          * Fires when this region is expanded.
29291          * @param {Roo.LayoutRegion} this
29292          */
29293         "expanded" : true,
29294         /**
29295          * @event slideshow
29296          * Fires when this region is slid into view.
29297          * @param {Roo.LayoutRegion} this
29298          */
29299         "slideshow" : true,
29300         /**
29301          * @event slidehide
29302          * Fires when this region slides out of view. 
29303          * @param {Roo.LayoutRegion} this
29304          */
29305         "slidehide" : true,
29306         /**
29307          * @event panelactivated
29308          * Fires when a panel is activated. 
29309          * @param {Roo.LayoutRegion} this
29310          * @param {Roo.ContentPanel} panel The activated panel
29311          */
29312         "panelactivated" : true,
29313         /**
29314          * @event resized
29315          * Fires when the user resizes this region. 
29316          * @param {Roo.LayoutRegion} this
29317          * @param {Number} newSize The new size (width for east/west, height for north/south)
29318          */
29319         "resized" : true
29320     };
29321     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29322     this.panels = new Roo.util.MixedCollection();
29323     this.panels.getKey = this.getPanelId.createDelegate(this);
29324     this.box = null;
29325     this.activePanel = null;
29326     // ensure listeners are added...
29327     
29328     if (config.listeners || config.events) {
29329         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29330             listeners : config.listeners || {},
29331             events : config.events || {}
29332         });
29333     }
29334     
29335     if(skipConfig !== true){
29336         this.applyConfig(config);
29337     }
29338 };
29339
29340 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29341     getPanelId : function(p){
29342         return p.getId();
29343     },
29344     
29345     applyConfig : function(config){
29346         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29347         this.config = config;
29348         
29349     },
29350     
29351     /**
29352      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29353      * the width, for horizontal (north, south) the height.
29354      * @param {Number} newSize The new width or height
29355      */
29356     resizeTo : function(newSize){
29357         var el = this.el ? this.el :
29358                  (this.activePanel ? this.activePanel.getEl() : null);
29359         if(el){
29360             switch(this.position){
29361                 case "east":
29362                 case "west":
29363                     el.setWidth(newSize);
29364                     this.fireEvent("resized", this, newSize);
29365                 break;
29366                 case "north":
29367                 case "south":
29368                     el.setHeight(newSize);
29369                     this.fireEvent("resized", this, newSize);
29370                 break;                
29371             }
29372         }
29373     },
29374     
29375     getBox : function(){
29376         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29377     },
29378     
29379     getMargins : function(){
29380         return this.margins;
29381     },
29382     
29383     updateBox : function(box){
29384         this.box = box;
29385         var el = this.activePanel.getEl();
29386         el.dom.style.left = box.x + "px";
29387         el.dom.style.top = box.y + "px";
29388         this.activePanel.setSize(box.width, box.height);
29389     },
29390     
29391     /**
29392      * Returns the container element for this region.
29393      * @return {Roo.Element}
29394      */
29395     getEl : function(){
29396         return this.activePanel;
29397     },
29398     
29399     /**
29400      * Returns true if this region is currently visible.
29401      * @return {Boolean}
29402      */
29403     isVisible : function(){
29404         return this.activePanel ? true : false;
29405     },
29406     
29407     setActivePanel : function(panel){
29408         panel = this.getPanel(panel);
29409         if(this.activePanel && this.activePanel != panel){
29410             this.activePanel.setActiveState(false);
29411             this.activePanel.getEl().setLeftTop(-10000,-10000);
29412         }
29413         this.activePanel = panel;
29414         panel.setActiveState(true);
29415         if(this.box){
29416             panel.setSize(this.box.width, this.box.height);
29417         }
29418         this.fireEvent("panelactivated", this, panel);
29419         this.fireEvent("invalidated");
29420     },
29421     
29422     /**
29423      * Show the specified panel.
29424      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29425      * @return {Roo.ContentPanel} The shown panel or null
29426      */
29427     showPanel : function(panel){
29428         if(panel = this.getPanel(panel)){
29429             this.setActivePanel(panel);
29430         }
29431         return panel;
29432     },
29433     
29434     /**
29435      * Get the active panel for this region.
29436      * @return {Roo.ContentPanel} The active panel or null
29437      */
29438     getActivePanel : function(){
29439         return this.activePanel;
29440     },
29441     
29442     /**
29443      * Add the passed ContentPanel(s)
29444      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29445      * @return {Roo.ContentPanel} The panel added (if only one was added)
29446      */
29447     add : function(panel){
29448         if(arguments.length > 1){
29449             for(var i = 0, len = arguments.length; i < len; i++) {
29450                 this.add(arguments[i]);
29451             }
29452             return null;
29453         }
29454         if(this.hasPanel(panel)){
29455             this.showPanel(panel);
29456             return panel;
29457         }
29458         var el = panel.getEl();
29459         if(el.dom.parentNode != this.mgr.el.dom){
29460             this.mgr.el.dom.appendChild(el.dom);
29461         }
29462         if(panel.setRegion){
29463             panel.setRegion(this);
29464         }
29465         this.panels.add(panel);
29466         el.setStyle("position", "absolute");
29467         if(!panel.background){
29468             this.setActivePanel(panel);
29469             if(this.config.initialSize && this.panels.getCount()==1){
29470                 this.resizeTo(this.config.initialSize);
29471             }
29472         }
29473         this.fireEvent("paneladded", this, panel);
29474         return panel;
29475     },
29476     
29477     /**
29478      * Returns true if the panel is in this region.
29479      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29480      * @return {Boolean}
29481      */
29482     hasPanel : function(panel){
29483         if(typeof panel == "object"){ // must be panel obj
29484             panel = panel.getId();
29485         }
29486         return this.getPanel(panel) ? true : false;
29487     },
29488     
29489     /**
29490      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29491      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29492      * @param {Boolean} preservePanel Overrides the config preservePanel option
29493      * @return {Roo.ContentPanel} The panel that was removed
29494      */
29495     remove : function(panel, preservePanel){
29496         panel = this.getPanel(panel);
29497         if(!panel){
29498             return null;
29499         }
29500         var e = {};
29501         this.fireEvent("beforeremove", this, panel, e);
29502         if(e.cancel === true){
29503             return null;
29504         }
29505         var panelId = panel.getId();
29506         this.panels.removeKey(panelId);
29507         return panel;
29508     },
29509     
29510     /**
29511      * Returns the panel specified or null if it's not in this region.
29512      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29513      * @return {Roo.ContentPanel}
29514      */
29515     getPanel : function(id){
29516         if(typeof id == "object"){ // must be panel obj
29517             return id;
29518         }
29519         return this.panels.get(id);
29520     },
29521     
29522     /**
29523      * Returns this regions position (north/south/east/west/center).
29524      * @return {String} 
29525      */
29526     getPosition: function(){
29527         return this.position;    
29528     }
29529 });/*
29530  * Based on:
29531  * Ext JS Library 1.1.1
29532  * Copyright(c) 2006-2007, Ext JS, LLC.
29533  *
29534  * Originally Released Under LGPL - original licence link has changed is not relivant.
29535  *
29536  * Fork - LGPL
29537  * <script type="text/javascript">
29538  */
29539  
29540 /**
29541  * @class Roo.LayoutRegion
29542  * @extends Roo.BasicLayoutRegion
29543  * This class represents a region in a layout manager.
29544  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
29545  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
29546  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
29547  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29548  * @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})
29549  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
29550  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
29551  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
29552  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
29553  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
29554  * @cfg {String}    title           The title for the region (overrides panel titles)
29555  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
29556  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29557  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
29558  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29559  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
29560  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29561  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
29562  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
29563  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
29564  * @cfg {Boolean}   showPin         True to show a pin button
29565  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
29566  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
29567  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
29568  * @cfg {Number}    width           For East/West panels
29569  * @cfg {Number}    height          For North/South panels
29570  * @cfg {Boolean}   split           To show the splitter
29571  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
29572  */
29573 Roo.LayoutRegion = function(mgr, config, pos){
29574     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29575     var dh = Roo.DomHelper;
29576     /** This region's container element 
29577     * @type Roo.Element */
29578     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29579     /** This region's title element 
29580     * @type Roo.Element */
29581
29582     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29583         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
29584         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29585     ]}, true);
29586     this.titleEl.enableDisplayMode();
29587     /** This region's title text element 
29588     * @type HTMLElement */
29589     this.titleTextEl = this.titleEl.dom.firstChild;
29590     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
29591     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
29592     this.closeBtn.enableDisplayMode();
29593     this.closeBtn.on("click", this.closeClicked, this);
29594     this.closeBtn.hide();
29595
29596     this.createBody(config);
29597     this.visible = true;
29598     this.collapsed = false;
29599
29600     if(config.hideWhenEmpty){
29601         this.hide();
29602         this.on("paneladded", this.validateVisibility, this);
29603         this.on("panelremoved", this.validateVisibility, this);
29604     }
29605     this.applyConfig(config);
29606 };
29607
29608 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
29609
29610     createBody : function(){
29611         /** This region's body element 
29612         * @type Roo.Element */
29613         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
29614     },
29615
29616     applyConfig : function(c){
29617         if(c.collapsible && this.position != "center" && !this.collapsedEl){
29618             var dh = Roo.DomHelper;
29619             if(c.titlebar !== false){
29620                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
29621                 this.collapseBtn.on("click", this.collapse, this);
29622                 this.collapseBtn.enableDisplayMode();
29623
29624                 if(c.showPin === true || this.showPin){
29625                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
29626                     this.stickBtn.enableDisplayMode();
29627                     this.stickBtn.on("click", this.expand, this);
29628                     this.stickBtn.hide();
29629                 }
29630             }
29631             /** This region's collapsed element
29632             * @type Roo.Element */
29633             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
29634                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
29635             ]}, true);
29636             if(c.floatable !== false){
29637                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
29638                this.collapsedEl.on("click", this.collapseClick, this);
29639             }
29640
29641             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
29642                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
29643                    id: "message", unselectable: "on", style:{"float":"left"}});
29644                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
29645              }
29646             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
29647             this.expandBtn.on("click", this.expand, this);
29648         }
29649         if(this.collapseBtn){
29650             this.collapseBtn.setVisible(c.collapsible == true);
29651         }
29652         this.cmargins = c.cmargins || this.cmargins ||
29653                          (this.position == "west" || this.position == "east" ?
29654                              {top: 0, left: 2, right:2, bottom: 0} :
29655                              {top: 2, left: 0, right:0, bottom: 2});
29656         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29657         this.bottomTabs = c.tabPosition != "top";
29658         this.autoScroll = c.autoScroll || false;
29659         if(this.autoScroll){
29660             this.bodyEl.setStyle("overflow", "auto");
29661         }else{
29662             this.bodyEl.setStyle("overflow", "hidden");
29663         }
29664         //if(c.titlebar !== false){
29665             if((!c.titlebar && !c.title) || c.titlebar === false){
29666                 this.titleEl.hide();
29667             }else{
29668                 this.titleEl.show();
29669                 if(c.title){
29670                     this.titleTextEl.innerHTML = c.title;
29671                 }
29672             }
29673         //}
29674         this.duration = c.duration || .30;
29675         this.slideDuration = c.slideDuration || .45;
29676         this.config = c;
29677         if(c.collapsed){
29678             this.collapse(true);
29679         }
29680         if(c.hidden){
29681             this.hide();
29682         }
29683     },
29684     /**
29685      * Returns true if this region is currently visible.
29686      * @return {Boolean}
29687      */
29688     isVisible : function(){
29689         return this.visible;
29690     },
29691
29692     /**
29693      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
29694      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
29695      */
29696     setCollapsedTitle : function(title){
29697         title = title || "&#160;";
29698         if(this.collapsedTitleTextEl){
29699             this.collapsedTitleTextEl.innerHTML = title;
29700         }
29701     },
29702
29703     getBox : function(){
29704         var b;
29705         if(!this.collapsed){
29706             b = this.el.getBox(false, true);
29707         }else{
29708             b = this.collapsedEl.getBox(false, true);
29709         }
29710         return b;
29711     },
29712
29713     getMargins : function(){
29714         return this.collapsed ? this.cmargins : this.margins;
29715     },
29716
29717     highlight : function(){
29718         this.el.addClass("x-layout-panel-dragover");
29719     },
29720
29721     unhighlight : function(){
29722         this.el.removeClass("x-layout-panel-dragover");
29723     },
29724
29725     updateBox : function(box){
29726         this.box = box;
29727         if(!this.collapsed){
29728             this.el.dom.style.left = box.x + "px";
29729             this.el.dom.style.top = box.y + "px";
29730             this.updateBody(box.width, box.height);
29731         }else{
29732             this.collapsedEl.dom.style.left = box.x + "px";
29733             this.collapsedEl.dom.style.top = box.y + "px";
29734             this.collapsedEl.setSize(box.width, box.height);
29735         }
29736         if(this.tabs){
29737             this.tabs.autoSizeTabs();
29738         }
29739     },
29740
29741     updateBody : function(w, h){
29742         if(w !== null){
29743             this.el.setWidth(w);
29744             w -= this.el.getBorderWidth("rl");
29745             if(this.config.adjustments){
29746                 w += this.config.adjustments[0];
29747             }
29748         }
29749         if(h !== null){
29750             this.el.setHeight(h);
29751             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
29752             h -= this.el.getBorderWidth("tb");
29753             if(this.config.adjustments){
29754                 h += this.config.adjustments[1];
29755             }
29756             this.bodyEl.setHeight(h);
29757             if(this.tabs){
29758                 h = this.tabs.syncHeight(h);
29759             }
29760         }
29761         if(this.panelSize){
29762             w = w !== null ? w : this.panelSize.width;
29763             h = h !== null ? h : this.panelSize.height;
29764         }
29765         if(this.activePanel){
29766             var el = this.activePanel.getEl();
29767             w = w !== null ? w : el.getWidth();
29768             h = h !== null ? h : el.getHeight();
29769             this.panelSize = {width: w, height: h};
29770             this.activePanel.setSize(w, h);
29771         }
29772         if(Roo.isIE && this.tabs){
29773             this.tabs.el.repaint();
29774         }
29775     },
29776
29777     /**
29778      * Returns the container element for this region.
29779      * @return {Roo.Element}
29780      */
29781     getEl : function(){
29782         return this.el;
29783     },
29784
29785     /**
29786      * Hides this region.
29787      */
29788     hide : function(){
29789         if(!this.collapsed){
29790             this.el.dom.style.left = "-2000px";
29791             this.el.hide();
29792         }else{
29793             this.collapsedEl.dom.style.left = "-2000px";
29794             this.collapsedEl.hide();
29795         }
29796         this.visible = false;
29797         this.fireEvent("visibilitychange", this, false);
29798     },
29799
29800     /**
29801      * Shows this region if it was previously hidden.
29802      */
29803     show : function(){
29804         if(!this.collapsed){
29805             this.el.show();
29806         }else{
29807             this.collapsedEl.show();
29808         }
29809         this.visible = true;
29810         this.fireEvent("visibilitychange", this, true);
29811     },
29812
29813     closeClicked : function(){
29814         if(this.activePanel){
29815             this.remove(this.activePanel);
29816         }
29817     },
29818
29819     collapseClick : function(e){
29820         if(this.isSlid){
29821            e.stopPropagation();
29822            this.slideIn();
29823         }else{
29824            e.stopPropagation();
29825            this.slideOut();
29826         }
29827     },
29828
29829     /**
29830      * Collapses this region.
29831      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
29832      */
29833     collapse : function(skipAnim, skipCheck = false){
29834         if(this.collapsed) {
29835             return;
29836         }
29837         
29838         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
29839             
29840             this.collapsed = true;
29841             if(this.split){
29842                 this.split.el.hide();
29843             }
29844             if(this.config.animate && skipAnim !== true){
29845                 this.fireEvent("invalidated", this);
29846                 this.animateCollapse();
29847             }else{
29848                 this.el.setLocation(-20000,-20000);
29849                 this.el.hide();
29850                 this.collapsedEl.show();
29851                 this.fireEvent("collapsed", this);
29852                 this.fireEvent("invalidated", this);
29853             }
29854         }
29855         
29856     },
29857
29858     animateCollapse : function(){
29859         // overridden
29860     },
29861
29862     /**
29863      * Expands this region if it was previously collapsed.
29864      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
29865      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
29866      */
29867     expand : function(e, skipAnim){
29868         if(e) {
29869             e.stopPropagation();
29870         }
29871         if(!this.collapsed || this.el.hasActiveFx()) {
29872             return;
29873         }
29874         if(this.isSlid){
29875             this.afterSlideIn();
29876             skipAnim = true;
29877         }
29878         this.collapsed = false;
29879         if(this.config.animate && skipAnim !== true){
29880             this.animateExpand();
29881         }else{
29882             this.el.show();
29883             if(this.split){
29884                 this.split.el.show();
29885             }
29886             this.collapsedEl.setLocation(-2000,-2000);
29887             this.collapsedEl.hide();
29888             this.fireEvent("invalidated", this);
29889             this.fireEvent("expanded", this);
29890         }
29891     },
29892
29893     animateExpand : function(){
29894         // overridden
29895     },
29896
29897     initTabs : function()
29898     {
29899         this.bodyEl.setStyle("overflow", "hidden");
29900         var ts = new Roo.TabPanel(
29901                 this.bodyEl.dom,
29902                 {
29903                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
29904                     disableTooltips: this.config.disableTabTips,
29905                     toolbar : this.config.toolbar
29906                 }
29907         );
29908         if(this.config.hideTabs){
29909             ts.stripWrap.setDisplayed(false);
29910         }
29911         this.tabs = ts;
29912         ts.resizeTabs = this.config.resizeTabs === true;
29913         ts.minTabWidth = this.config.minTabWidth || 40;
29914         ts.maxTabWidth = this.config.maxTabWidth || 250;
29915         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
29916         ts.monitorResize = false;
29917         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
29918         ts.bodyEl.addClass('x-layout-tabs-body');
29919         this.panels.each(this.initPanelAsTab, this);
29920     },
29921
29922     initPanelAsTab : function(panel){
29923         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
29924                     this.config.closeOnTab && panel.isClosable());
29925         if(panel.tabTip !== undefined){
29926             ti.setTooltip(panel.tabTip);
29927         }
29928         ti.on("activate", function(){
29929               this.setActivePanel(panel);
29930         }, this);
29931         if(this.config.closeOnTab){
29932             ti.on("beforeclose", function(t, e){
29933                 e.cancel = true;
29934                 this.remove(panel);
29935             }, this);
29936         }
29937         return ti;
29938     },
29939
29940     updatePanelTitle : function(panel, title){
29941         if(this.activePanel == panel){
29942             this.updateTitle(title);
29943         }
29944         if(this.tabs){
29945             var ti = this.tabs.getTab(panel.getEl().id);
29946             ti.setText(title);
29947             if(panel.tabTip !== undefined){
29948                 ti.setTooltip(panel.tabTip);
29949             }
29950         }
29951     },
29952
29953     updateTitle : function(title){
29954         if(this.titleTextEl && !this.config.title){
29955             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
29956         }
29957     },
29958
29959     setActivePanel : function(panel){
29960         panel = this.getPanel(panel);
29961         if(this.activePanel && this.activePanel != panel){
29962             this.activePanel.setActiveState(false);
29963         }
29964         this.activePanel = panel;
29965         panel.setActiveState(true);
29966         if(this.panelSize){
29967             panel.setSize(this.panelSize.width, this.panelSize.height);
29968         }
29969         if(this.closeBtn){
29970             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
29971         }
29972         this.updateTitle(panel.getTitle());
29973         if(this.tabs){
29974             this.fireEvent("invalidated", this);
29975         }
29976         this.fireEvent("panelactivated", this, panel);
29977     },
29978
29979     /**
29980      * Shows the specified panel.
29981      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
29982      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
29983      */
29984     showPanel : function(panel)
29985     {
29986         panel = this.getPanel(panel);
29987         if(panel){
29988             if(this.tabs){
29989                 var tab = this.tabs.getTab(panel.getEl().id);
29990                 if(tab.isHidden()){
29991                     this.tabs.unhideTab(tab.id);
29992                 }
29993                 tab.activate();
29994             }else{
29995                 this.setActivePanel(panel);
29996             }
29997         }
29998         return panel;
29999     },
30000
30001     /**
30002      * Get the active panel for this region.
30003      * @return {Roo.ContentPanel} The active panel or null
30004      */
30005     getActivePanel : function(){
30006         return this.activePanel;
30007     },
30008
30009     validateVisibility : function(){
30010         if(this.panels.getCount() < 1){
30011             this.updateTitle("&#160;");
30012             this.closeBtn.hide();
30013             this.hide();
30014         }else{
30015             if(!this.isVisible()){
30016                 this.show();
30017             }
30018         }
30019     },
30020
30021     /**
30022      * Adds the passed ContentPanel(s) to this region.
30023      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30024      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30025      */
30026     add : function(panel){
30027         if(arguments.length > 1){
30028             for(var i = 0, len = arguments.length; i < len; i++) {
30029                 this.add(arguments[i]);
30030             }
30031             return null;
30032         }
30033         if(this.hasPanel(panel)){
30034             this.showPanel(panel);
30035             return panel;
30036         }
30037         panel.setRegion(this);
30038         this.panels.add(panel);
30039         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30040             this.bodyEl.dom.appendChild(panel.getEl().dom);
30041             if(panel.background !== true){
30042                 this.setActivePanel(panel);
30043             }
30044             this.fireEvent("paneladded", this, panel);
30045             return panel;
30046         }
30047         if(!this.tabs){
30048             this.initTabs();
30049         }else{
30050             this.initPanelAsTab(panel);
30051         }
30052         if(panel.background !== true){
30053             this.tabs.activate(panel.getEl().id);
30054         }
30055         this.fireEvent("paneladded", this, panel);
30056         return panel;
30057     },
30058
30059     /**
30060      * Hides the tab for the specified panel.
30061      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30062      */
30063     hidePanel : function(panel){
30064         if(this.tabs && (panel = this.getPanel(panel))){
30065             this.tabs.hideTab(panel.getEl().id);
30066         }
30067     },
30068
30069     /**
30070      * Unhides the tab for a previously hidden panel.
30071      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30072      */
30073     unhidePanel : function(panel){
30074         if(this.tabs && (panel = this.getPanel(panel))){
30075             this.tabs.unhideTab(panel.getEl().id);
30076         }
30077     },
30078
30079     clearPanels : function(){
30080         while(this.panels.getCount() > 0){
30081              this.remove(this.panels.first());
30082         }
30083     },
30084
30085     /**
30086      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30087      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30088      * @param {Boolean} preservePanel Overrides the config preservePanel option
30089      * @return {Roo.ContentPanel} The panel that was removed
30090      */
30091     remove : function(panel, preservePanel){
30092         panel = this.getPanel(panel);
30093         if(!panel){
30094             return null;
30095         }
30096         var e = {};
30097         this.fireEvent("beforeremove", this, panel, e);
30098         if(e.cancel === true){
30099             return null;
30100         }
30101         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30102         var panelId = panel.getId();
30103         this.panels.removeKey(panelId);
30104         if(preservePanel){
30105             document.body.appendChild(panel.getEl().dom);
30106         }
30107         if(this.tabs){
30108             this.tabs.removeTab(panel.getEl().id);
30109         }else if (!preservePanel){
30110             this.bodyEl.dom.removeChild(panel.getEl().dom);
30111         }
30112         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30113             var p = this.panels.first();
30114             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30115             tempEl.appendChild(p.getEl().dom);
30116             this.bodyEl.update("");
30117             this.bodyEl.dom.appendChild(p.getEl().dom);
30118             tempEl = null;
30119             this.updateTitle(p.getTitle());
30120             this.tabs = null;
30121             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30122             this.setActivePanel(p);
30123         }
30124         panel.setRegion(null);
30125         if(this.activePanel == panel){
30126             this.activePanel = null;
30127         }
30128         if(this.config.autoDestroy !== false && preservePanel !== true){
30129             try{panel.destroy();}catch(e){}
30130         }
30131         this.fireEvent("panelremoved", this, panel);
30132         return panel;
30133     },
30134
30135     /**
30136      * Returns the TabPanel component used by this region
30137      * @return {Roo.TabPanel}
30138      */
30139     getTabs : function(){
30140         return this.tabs;
30141     },
30142
30143     createTool : function(parentEl, className){
30144         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30145             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30146         btn.addClassOnOver("x-layout-tools-button-over");
30147         return btn;
30148     }
30149 });/*
30150  * Based on:
30151  * Ext JS Library 1.1.1
30152  * Copyright(c) 2006-2007, Ext JS, LLC.
30153  *
30154  * Originally Released Under LGPL - original licence link has changed is not relivant.
30155  *
30156  * Fork - LGPL
30157  * <script type="text/javascript">
30158  */
30159  
30160
30161
30162 /**
30163  * @class Roo.SplitLayoutRegion
30164  * @extends Roo.LayoutRegion
30165  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30166  */
30167 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30168     this.cursor = cursor;
30169     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30170 };
30171
30172 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30173     splitTip : "Drag to resize.",
30174     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30175     useSplitTips : false,
30176
30177     applyConfig : function(config){
30178         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30179         if(config.split){
30180             if(!this.split){
30181                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30182                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30183                 /** The SplitBar for this region 
30184                 * @type Roo.SplitBar */
30185                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30186                 this.split.on("moved", this.onSplitMove, this);
30187                 this.split.useShim = config.useShim === true;
30188                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30189                 if(this.useSplitTips){
30190                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30191                 }
30192                 if(config.collapsible){
30193                     this.split.el.on("dblclick", this.collapse,  this);
30194                 }
30195             }
30196             if(typeof config.minSize != "undefined"){
30197                 this.split.minSize = config.minSize;
30198             }
30199             if(typeof config.maxSize != "undefined"){
30200                 this.split.maxSize = config.maxSize;
30201             }
30202             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30203                 this.hideSplitter();
30204             }
30205         }
30206     },
30207
30208     getHMaxSize : function(){
30209          var cmax = this.config.maxSize || 10000;
30210          var center = this.mgr.getRegion("center");
30211          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30212     },
30213
30214     getVMaxSize : function(){
30215          var cmax = this.config.maxSize || 10000;
30216          var center = this.mgr.getRegion("center");
30217          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30218     },
30219
30220     onSplitMove : function(split, newSize){
30221         this.fireEvent("resized", this, newSize);
30222     },
30223     
30224     /** 
30225      * Returns the {@link Roo.SplitBar} for this region.
30226      * @return {Roo.SplitBar}
30227      */
30228     getSplitBar : function(){
30229         return this.split;
30230     },
30231     
30232     hide : function(){
30233         this.hideSplitter();
30234         Roo.SplitLayoutRegion.superclass.hide.call(this);
30235     },
30236
30237     hideSplitter : function(){
30238         if(this.split){
30239             this.split.el.setLocation(-2000,-2000);
30240             this.split.el.hide();
30241         }
30242     },
30243
30244     show : function(){
30245         if(this.split){
30246             this.split.el.show();
30247         }
30248         Roo.SplitLayoutRegion.superclass.show.call(this);
30249     },
30250     
30251     beforeSlide: function(){
30252         if(Roo.isGecko){// firefox overflow auto bug workaround
30253             this.bodyEl.clip();
30254             if(this.tabs) {
30255                 this.tabs.bodyEl.clip();
30256             }
30257             if(this.activePanel){
30258                 this.activePanel.getEl().clip();
30259                 
30260                 if(this.activePanel.beforeSlide){
30261                     this.activePanel.beforeSlide();
30262                 }
30263             }
30264         }
30265     },
30266     
30267     afterSlide : function(){
30268         if(Roo.isGecko){// firefox overflow auto bug workaround
30269             this.bodyEl.unclip();
30270             if(this.tabs) {
30271                 this.tabs.bodyEl.unclip();
30272             }
30273             if(this.activePanel){
30274                 this.activePanel.getEl().unclip();
30275                 if(this.activePanel.afterSlide){
30276                     this.activePanel.afterSlide();
30277                 }
30278             }
30279         }
30280     },
30281
30282     initAutoHide : function(){
30283         if(this.autoHide !== false){
30284             if(!this.autoHideHd){
30285                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30286                 this.autoHideHd = {
30287                     "mouseout": function(e){
30288                         if(!e.within(this.el, true)){
30289                             st.delay(500);
30290                         }
30291                     },
30292                     "mouseover" : function(e){
30293                         st.cancel();
30294                     },
30295                     scope : this
30296                 };
30297             }
30298             this.el.on(this.autoHideHd);
30299         }
30300     },
30301
30302     clearAutoHide : function(){
30303         if(this.autoHide !== false){
30304             this.el.un("mouseout", this.autoHideHd.mouseout);
30305             this.el.un("mouseover", this.autoHideHd.mouseover);
30306         }
30307     },
30308
30309     clearMonitor : function(){
30310         Roo.get(document).un("click", this.slideInIf, this);
30311     },
30312
30313     // these names are backwards but not changed for compat
30314     slideOut : function(){
30315         if(this.isSlid || this.el.hasActiveFx()){
30316             return;
30317         }
30318         this.isSlid = true;
30319         if(this.collapseBtn){
30320             this.collapseBtn.hide();
30321         }
30322         this.closeBtnState = this.closeBtn.getStyle('display');
30323         this.closeBtn.hide();
30324         if(this.stickBtn){
30325             this.stickBtn.show();
30326         }
30327         this.el.show();
30328         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30329         this.beforeSlide();
30330         this.el.setStyle("z-index", 10001);
30331         this.el.slideIn(this.getSlideAnchor(), {
30332             callback: function(){
30333                 this.afterSlide();
30334                 this.initAutoHide();
30335                 Roo.get(document).on("click", this.slideInIf, this);
30336                 this.fireEvent("slideshow", this);
30337             },
30338             scope: this,
30339             block: true
30340         });
30341     },
30342
30343     afterSlideIn : function(){
30344         this.clearAutoHide();
30345         this.isSlid = false;
30346         this.clearMonitor();
30347         this.el.setStyle("z-index", "");
30348         if(this.collapseBtn){
30349             this.collapseBtn.show();
30350         }
30351         this.closeBtn.setStyle('display', this.closeBtnState);
30352         if(this.stickBtn){
30353             this.stickBtn.hide();
30354         }
30355         this.fireEvent("slidehide", this);
30356     },
30357
30358     slideIn : function(cb){
30359         if(!this.isSlid || this.el.hasActiveFx()){
30360             Roo.callback(cb);
30361             return;
30362         }
30363         this.isSlid = false;
30364         this.beforeSlide();
30365         this.el.slideOut(this.getSlideAnchor(), {
30366             callback: function(){
30367                 this.el.setLeftTop(-10000, -10000);
30368                 this.afterSlide();
30369                 this.afterSlideIn();
30370                 Roo.callback(cb);
30371             },
30372             scope: this,
30373             block: true
30374         });
30375     },
30376     
30377     slideInIf : function(e){
30378         if(!e.within(this.el)){
30379             this.slideIn();
30380         }
30381     },
30382
30383     animateCollapse : function(){
30384         this.beforeSlide();
30385         this.el.setStyle("z-index", 20000);
30386         var anchor = this.getSlideAnchor();
30387         this.el.slideOut(anchor, {
30388             callback : function(){
30389                 this.el.setStyle("z-index", "");
30390                 this.collapsedEl.slideIn(anchor, {duration:.3});
30391                 this.afterSlide();
30392                 this.el.setLocation(-10000,-10000);
30393                 this.el.hide();
30394                 this.fireEvent("collapsed", this);
30395             },
30396             scope: this,
30397             block: true
30398         });
30399     },
30400
30401     animateExpand : function(){
30402         this.beforeSlide();
30403         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30404         this.el.setStyle("z-index", 20000);
30405         this.collapsedEl.hide({
30406             duration:.1
30407         });
30408         this.el.slideIn(this.getSlideAnchor(), {
30409             callback : function(){
30410                 this.el.setStyle("z-index", "");
30411                 this.afterSlide();
30412                 if(this.split){
30413                     this.split.el.show();
30414                 }
30415                 this.fireEvent("invalidated", this);
30416                 this.fireEvent("expanded", this);
30417             },
30418             scope: this,
30419             block: true
30420         });
30421     },
30422
30423     anchors : {
30424         "west" : "left",
30425         "east" : "right",
30426         "north" : "top",
30427         "south" : "bottom"
30428     },
30429
30430     sanchors : {
30431         "west" : "l",
30432         "east" : "r",
30433         "north" : "t",
30434         "south" : "b"
30435     },
30436
30437     canchors : {
30438         "west" : "tl-tr",
30439         "east" : "tr-tl",
30440         "north" : "tl-bl",
30441         "south" : "bl-tl"
30442     },
30443
30444     getAnchor : function(){
30445         return this.anchors[this.position];
30446     },
30447
30448     getCollapseAnchor : function(){
30449         return this.canchors[this.position];
30450     },
30451
30452     getSlideAnchor : function(){
30453         return this.sanchors[this.position];
30454     },
30455
30456     getAlignAdj : function(){
30457         var cm = this.cmargins;
30458         switch(this.position){
30459             case "west":
30460                 return [0, 0];
30461             break;
30462             case "east":
30463                 return [0, 0];
30464             break;
30465             case "north":
30466                 return [0, 0];
30467             break;
30468             case "south":
30469                 return [0, 0];
30470             break;
30471         }
30472     },
30473
30474     getExpandAdj : function(){
30475         var c = this.collapsedEl, cm = this.cmargins;
30476         switch(this.position){
30477             case "west":
30478                 return [-(cm.right+c.getWidth()+cm.left), 0];
30479             break;
30480             case "east":
30481                 return [cm.right+c.getWidth()+cm.left, 0];
30482             break;
30483             case "north":
30484                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30485             break;
30486             case "south":
30487                 return [0, cm.top+cm.bottom+c.getHeight()];
30488             break;
30489         }
30490     }
30491 });/*
30492  * Based on:
30493  * Ext JS Library 1.1.1
30494  * Copyright(c) 2006-2007, Ext JS, LLC.
30495  *
30496  * Originally Released Under LGPL - original licence link has changed is not relivant.
30497  *
30498  * Fork - LGPL
30499  * <script type="text/javascript">
30500  */
30501 /*
30502  * These classes are private internal classes
30503  */
30504 Roo.CenterLayoutRegion = function(mgr, config){
30505     Roo.LayoutRegion.call(this, mgr, config, "center");
30506     this.visible = true;
30507     this.minWidth = config.minWidth || 20;
30508     this.minHeight = config.minHeight || 20;
30509 };
30510
30511 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30512     hide : function(){
30513         // center panel can't be hidden
30514     },
30515     
30516     show : function(){
30517         // center panel can't be hidden
30518     },
30519     
30520     getMinWidth: function(){
30521         return this.minWidth;
30522     },
30523     
30524     getMinHeight: function(){
30525         return this.minHeight;
30526     }
30527 });
30528
30529
30530 Roo.NorthLayoutRegion = function(mgr, config){
30531     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30532     if(this.split){
30533         this.split.placement = Roo.SplitBar.TOP;
30534         this.split.orientation = Roo.SplitBar.VERTICAL;
30535         this.split.el.addClass("x-layout-split-v");
30536     }
30537     var size = config.initialSize || config.height;
30538     if(typeof size != "undefined"){
30539         this.el.setHeight(size);
30540     }
30541 };
30542 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30543     orientation: Roo.SplitBar.VERTICAL,
30544     getBox : function(){
30545         if(this.collapsed){
30546             return this.collapsedEl.getBox();
30547         }
30548         var box = this.el.getBox();
30549         if(this.split){
30550             box.height += this.split.el.getHeight();
30551         }
30552         return box;
30553     },
30554     
30555     updateBox : function(box){
30556         if(this.split && !this.collapsed){
30557             box.height -= this.split.el.getHeight();
30558             this.split.el.setLeft(box.x);
30559             this.split.el.setTop(box.y+box.height);
30560             this.split.el.setWidth(box.width);
30561         }
30562         if(this.collapsed){
30563             this.updateBody(box.width, null);
30564         }
30565         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30566     }
30567 });
30568
30569 Roo.SouthLayoutRegion = function(mgr, config){
30570     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30571     if(this.split){
30572         this.split.placement = Roo.SplitBar.BOTTOM;
30573         this.split.orientation = Roo.SplitBar.VERTICAL;
30574         this.split.el.addClass("x-layout-split-v");
30575     }
30576     var size = config.initialSize || config.height;
30577     if(typeof size != "undefined"){
30578         this.el.setHeight(size);
30579     }
30580 };
30581 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30582     orientation: Roo.SplitBar.VERTICAL,
30583     getBox : function(){
30584         if(this.collapsed){
30585             return this.collapsedEl.getBox();
30586         }
30587         var box = this.el.getBox();
30588         if(this.split){
30589             var sh = this.split.el.getHeight();
30590             box.height += sh;
30591             box.y -= sh;
30592         }
30593         return box;
30594     },
30595     
30596     updateBox : function(box){
30597         if(this.split && !this.collapsed){
30598             var sh = this.split.el.getHeight();
30599             box.height -= sh;
30600             box.y += sh;
30601             this.split.el.setLeft(box.x);
30602             this.split.el.setTop(box.y-sh);
30603             this.split.el.setWidth(box.width);
30604         }
30605         if(this.collapsed){
30606             this.updateBody(box.width, null);
30607         }
30608         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30609     }
30610 });
30611
30612 Roo.EastLayoutRegion = function(mgr, config){
30613     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
30614     if(this.split){
30615         this.split.placement = Roo.SplitBar.RIGHT;
30616         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30617         this.split.el.addClass("x-layout-split-h");
30618     }
30619     var size = config.initialSize || config.width;
30620     if(typeof size != "undefined"){
30621         this.el.setWidth(size);
30622     }
30623 };
30624 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
30625     orientation: Roo.SplitBar.HORIZONTAL,
30626     getBox : function(){
30627         if(this.collapsed){
30628             return this.collapsedEl.getBox();
30629         }
30630         var box = this.el.getBox();
30631         if(this.split){
30632             var sw = this.split.el.getWidth();
30633             box.width += sw;
30634             box.x -= sw;
30635         }
30636         return box;
30637     },
30638
30639     updateBox : function(box){
30640         if(this.split && !this.collapsed){
30641             var sw = this.split.el.getWidth();
30642             box.width -= sw;
30643             this.split.el.setLeft(box.x);
30644             this.split.el.setTop(box.y);
30645             this.split.el.setHeight(box.height);
30646             box.x += sw;
30647         }
30648         if(this.collapsed){
30649             this.updateBody(null, box.height);
30650         }
30651         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30652     }
30653 });
30654
30655 Roo.WestLayoutRegion = function(mgr, config){
30656     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
30657     if(this.split){
30658         this.split.placement = Roo.SplitBar.LEFT;
30659         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30660         this.split.el.addClass("x-layout-split-h");
30661     }
30662     var size = config.initialSize || config.width;
30663     if(typeof size != "undefined"){
30664         this.el.setWidth(size);
30665     }
30666 };
30667 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
30668     orientation: Roo.SplitBar.HORIZONTAL,
30669     getBox : function(){
30670         if(this.collapsed){
30671             return this.collapsedEl.getBox();
30672         }
30673         var box = this.el.getBox();
30674         if(this.split){
30675             box.width += this.split.el.getWidth();
30676         }
30677         return box;
30678     },
30679     
30680     updateBox : function(box){
30681         if(this.split && !this.collapsed){
30682             var sw = this.split.el.getWidth();
30683             box.width -= sw;
30684             this.split.el.setLeft(box.x+box.width);
30685             this.split.el.setTop(box.y);
30686             this.split.el.setHeight(box.height);
30687         }
30688         if(this.collapsed){
30689             this.updateBody(null, box.height);
30690         }
30691         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30692     }
30693 });
30694 /*
30695  * Based on:
30696  * Ext JS Library 1.1.1
30697  * Copyright(c) 2006-2007, Ext JS, LLC.
30698  *
30699  * Originally Released Under LGPL - original licence link has changed is not relivant.
30700  *
30701  * Fork - LGPL
30702  * <script type="text/javascript">
30703  */
30704  
30705  
30706 /*
30707  * Private internal class for reading and applying state
30708  */
30709 Roo.LayoutStateManager = function(layout){
30710      // default empty state
30711      this.state = {
30712         north: {},
30713         south: {},
30714         east: {},
30715         west: {}       
30716     };
30717 };
30718
30719 Roo.LayoutStateManager.prototype = {
30720     init : function(layout, provider){
30721         this.provider = provider;
30722         var state = provider.get(layout.id+"-layout-state");
30723         if(state){
30724             var wasUpdating = layout.isUpdating();
30725             if(!wasUpdating){
30726                 layout.beginUpdate();
30727             }
30728             for(var key in state){
30729                 if(typeof state[key] != "function"){
30730                     var rstate = state[key];
30731                     var r = layout.getRegion(key);
30732                     if(r && rstate){
30733                         if(rstate.size){
30734                             r.resizeTo(rstate.size);
30735                         }
30736                         if(rstate.collapsed == true){
30737                             r.collapse(true);
30738                         }else{
30739                             r.expand(null, true);
30740                         }
30741                     }
30742                 }
30743             }
30744             if(!wasUpdating){
30745                 layout.endUpdate();
30746             }
30747             this.state = state; 
30748         }
30749         this.layout = layout;
30750         layout.on("regionresized", this.onRegionResized, this);
30751         layout.on("regioncollapsed", this.onRegionCollapsed, this);
30752         layout.on("regionexpanded", this.onRegionExpanded, this);
30753     },
30754     
30755     storeState : function(){
30756         this.provider.set(this.layout.id+"-layout-state", this.state);
30757     },
30758     
30759     onRegionResized : function(region, newSize){
30760         this.state[region.getPosition()].size = newSize;
30761         this.storeState();
30762     },
30763     
30764     onRegionCollapsed : function(region){
30765         this.state[region.getPosition()].collapsed = true;
30766         this.storeState();
30767     },
30768     
30769     onRegionExpanded : function(region){
30770         this.state[region.getPosition()].collapsed = false;
30771         this.storeState();
30772     }
30773 };/*
30774  * Based on:
30775  * Ext JS Library 1.1.1
30776  * Copyright(c) 2006-2007, Ext JS, LLC.
30777  *
30778  * Originally Released Under LGPL - original licence link has changed is not relivant.
30779  *
30780  * Fork - LGPL
30781  * <script type="text/javascript">
30782  */
30783 /**
30784  * @class Roo.ContentPanel
30785  * @extends Roo.util.Observable
30786  * A basic ContentPanel element.
30787  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
30788  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
30789  * @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
30790  * @cfg {Boolean}   closable      True if the panel can be closed/removed
30791  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
30792  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
30793  * @cfg {Toolbar}   toolbar       A toolbar for this panel
30794  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
30795  * @cfg {String} title          The title for this panel
30796  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
30797  * @cfg {String} url            Calls {@link #setUrl} with this value
30798  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
30799  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
30800  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
30801  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
30802
30803  * @constructor
30804  * Create a new ContentPanel.
30805  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
30806  * @param {String/Object} config A string to set only the title or a config object
30807  * @param {String} content (optional) Set the HTML content for this panel
30808  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
30809  */
30810 Roo.ContentPanel = function(el, config, content){
30811     
30812      
30813     /*
30814     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
30815         config = el;
30816         el = Roo.id();
30817     }
30818     if (config && config.parentLayout) { 
30819         el = config.parentLayout.el.createChild(); 
30820     }
30821     */
30822     if(el.autoCreate){ // xtype is available if this is called from factory
30823         config = el;
30824         el = Roo.id();
30825     }
30826     this.el = Roo.get(el);
30827     if(!this.el && config && config.autoCreate){
30828         if(typeof config.autoCreate == "object"){
30829             if(!config.autoCreate.id){
30830                 config.autoCreate.id = config.id||el;
30831             }
30832             this.el = Roo.DomHelper.append(document.body,
30833                         config.autoCreate, true);
30834         }else{
30835             this.el = Roo.DomHelper.append(document.body,
30836                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
30837         }
30838     }
30839     this.closable = false;
30840     this.loaded = false;
30841     this.active = false;
30842     if(typeof config == "string"){
30843         this.title = config;
30844     }else{
30845         Roo.apply(this, config);
30846     }
30847     
30848     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
30849         this.wrapEl = this.el.wrap();
30850         this.toolbar.container = this.el.insertSibling(false, 'before');
30851         this.toolbar = new Roo.Toolbar(this.toolbar);
30852     }
30853     
30854     // xtype created footer. - not sure if will work as we normally have to render first..
30855     if (this.footer && !this.footer.el && this.footer.xtype) {
30856         if (!this.wrapEl) {
30857             this.wrapEl = this.el.wrap();
30858         }
30859     
30860         this.footer.container = this.wrapEl.createChild();
30861          
30862         this.footer = Roo.factory(this.footer, Roo);
30863         
30864     }
30865     
30866     if(this.resizeEl){
30867         this.resizeEl = Roo.get(this.resizeEl, true);
30868     }else{
30869         this.resizeEl = this.el;
30870     }
30871     // handle view.xtype
30872     
30873  
30874     
30875     
30876     this.addEvents({
30877         /**
30878          * @event activate
30879          * Fires when this panel is activated. 
30880          * @param {Roo.ContentPanel} this
30881          */
30882         "activate" : true,
30883         /**
30884          * @event deactivate
30885          * Fires when this panel is activated. 
30886          * @param {Roo.ContentPanel} this
30887          */
30888         "deactivate" : true,
30889
30890         /**
30891          * @event resize
30892          * Fires when this panel is resized if fitToFrame is true.
30893          * @param {Roo.ContentPanel} this
30894          * @param {Number} width The width after any component adjustments
30895          * @param {Number} height The height after any component adjustments
30896          */
30897         "resize" : true,
30898         
30899          /**
30900          * @event render
30901          * Fires when this tab is created
30902          * @param {Roo.ContentPanel} this
30903          */
30904         "render" : true
30905         
30906         
30907         
30908     });
30909     
30910
30911     
30912     
30913     if(this.autoScroll){
30914         this.resizeEl.setStyle("overflow", "auto");
30915     } else {
30916         // fix randome scrolling
30917         this.el.on('scroll', function() {
30918             Roo.log('fix random scolling');
30919             this.scrollTo('top',0); 
30920         });
30921     }
30922     content = content || this.content;
30923     if(content){
30924         this.setContent(content);
30925     }
30926     if(config && config.url){
30927         this.setUrl(this.url, this.params, this.loadOnce);
30928     }
30929     
30930     
30931     
30932     Roo.ContentPanel.superclass.constructor.call(this);
30933     
30934     if (this.view && typeof(this.view.xtype) != 'undefined') {
30935         this.view.el = this.el.appendChild(document.createElement("div"));
30936         this.view = Roo.factory(this.view); 
30937         this.view.render  &&  this.view.render(false, '');  
30938     }
30939     
30940     
30941     this.fireEvent('render', this);
30942 };
30943
30944 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
30945     tabTip:'',
30946     setRegion : function(region){
30947         this.region = region;
30948         if(region){
30949            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
30950         }else{
30951            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
30952         } 
30953     },
30954     
30955     /**
30956      * Returns the toolbar for this Panel if one was configured. 
30957      * @return {Roo.Toolbar} 
30958      */
30959     getToolbar : function(){
30960         return this.toolbar;
30961     },
30962     
30963     setActiveState : function(active){
30964         this.active = active;
30965         if(!active){
30966             this.fireEvent("deactivate", this);
30967         }else{
30968             this.fireEvent("activate", this);
30969         }
30970     },
30971     /**
30972      * Updates this panel's element
30973      * @param {String} content The new content
30974      * @param {Boolean} loadScripts (optional) true to look for and process scripts
30975     */
30976     setContent : function(content, loadScripts){
30977         this.el.update(content, loadScripts);
30978     },
30979
30980     ignoreResize : function(w, h){
30981         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
30982             return true;
30983         }else{
30984             this.lastSize = {width: w, height: h};
30985             return false;
30986         }
30987     },
30988     /**
30989      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
30990      * @return {Roo.UpdateManager} The UpdateManager
30991      */
30992     getUpdateManager : function(){
30993         return this.el.getUpdateManager();
30994     },
30995      /**
30996      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
30997      * @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:
30998 <pre><code>
30999 panel.load({
31000     url: "your-url.php",
31001     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31002     callback: yourFunction,
31003     scope: yourObject, //(optional scope)
31004     discardUrl: false,
31005     nocache: false,
31006     text: "Loading...",
31007     timeout: 30,
31008     scripts: false
31009 });
31010 </code></pre>
31011      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31012      * 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.
31013      * @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}
31014      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31015      * @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.
31016      * @return {Roo.ContentPanel} this
31017      */
31018     load : function(){
31019         var um = this.el.getUpdateManager();
31020         um.update.apply(um, arguments);
31021         return this;
31022     },
31023
31024
31025     /**
31026      * 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.
31027      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31028      * @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)
31029      * @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)
31030      * @return {Roo.UpdateManager} The UpdateManager
31031      */
31032     setUrl : function(url, params, loadOnce){
31033         if(this.refreshDelegate){
31034             this.removeListener("activate", this.refreshDelegate);
31035         }
31036         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31037         this.on("activate", this.refreshDelegate);
31038         return this.el.getUpdateManager();
31039     },
31040     
31041     _handleRefresh : function(url, params, loadOnce){
31042         if(!loadOnce || !this.loaded){
31043             var updater = this.el.getUpdateManager();
31044             updater.update(url, params, this._setLoaded.createDelegate(this));
31045         }
31046     },
31047     
31048     _setLoaded : function(){
31049         this.loaded = true;
31050     }, 
31051     
31052     /**
31053      * Returns this panel's id
31054      * @return {String} 
31055      */
31056     getId : function(){
31057         return this.el.id;
31058     },
31059     
31060     /** 
31061      * Returns this panel's element - used by regiosn to add.
31062      * @return {Roo.Element} 
31063      */
31064     getEl : function(){
31065         return this.wrapEl || this.el;
31066     },
31067     
31068     adjustForComponents : function(width, height)
31069     {
31070         //Roo.log('adjustForComponents ');
31071         if(this.resizeEl != this.el){
31072             width -= this.el.getFrameWidth('lr');
31073             height -= this.el.getFrameWidth('tb');
31074         }
31075         if(this.toolbar){
31076             var te = this.toolbar.getEl();
31077             height -= te.getHeight();
31078             te.setWidth(width);
31079         }
31080         if(this.footer){
31081             var te = this.footer.getEl();
31082             Roo.log("footer:" + te.getHeight());
31083             
31084             height -= te.getHeight();
31085             te.setWidth(width);
31086         }
31087         
31088         
31089         if(this.adjustments){
31090             width += this.adjustments[0];
31091             height += this.adjustments[1];
31092         }
31093         return {"width": width, "height": height};
31094     },
31095     
31096     setSize : function(width, height){
31097         if(this.fitToFrame && !this.ignoreResize(width, height)){
31098             if(this.fitContainer && this.resizeEl != this.el){
31099                 this.el.setSize(width, height);
31100             }
31101             var size = this.adjustForComponents(width, height);
31102             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31103             this.fireEvent('resize', this, size.width, size.height);
31104         }
31105     },
31106     
31107     /**
31108      * Returns this panel's title
31109      * @return {String} 
31110      */
31111     getTitle : function(){
31112         return this.title;
31113     },
31114     
31115     /**
31116      * Set this panel's title
31117      * @param {String} title
31118      */
31119     setTitle : function(title){
31120         this.title = title;
31121         if(this.region){
31122             this.region.updatePanelTitle(this, title);
31123         }
31124     },
31125     
31126     /**
31127      * Returns true is this panel was configured to be closable
31128      * @return {Boolean} 
31129      */
31130     isClosable : function(){
31131         return this.closable;
31132     },
31133     
31134     beforeSlide : function(){
31135         this.el.clip();
31136         this.resizeEl.clip();
31137     },
31138     
31139     afterSlide : function(){
31140         this.el.unclip();
31141         this.resizeEl.unclip();
31142     },
31143     
31144     /**
31145      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31146      *   Will fail silently if the {@link #setUrl} method has not been called.
31147      *   This does not activate the panel, just updates its content.
31148      */
31149     refresh : function(){
31150         if(this.refreshDelegate){
31151            this.loaded = false;
31152            this.refreshDelegate();
31153         }
31154     },
31155     
31156     /**
31157      * Destroys this panel
31158      */
31159     destroy : function(){
31160         this.el.removeAllListeners();
31161         var tempEl = document.createElement("span");
31162         tempEl.appendChild(this.el.dom);
31163         tempEl.innerHTML = "";
31164         this.el.remove();
31165         this.el = null;
31166     },
31167     
31168     /**
31169      * form - if the content panel contains a form - this is a reference to it.
31170      * @type {Roo.form.Form}
31171      */
31172     form : false,
31173     /**
31174      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31175      *    This contains a reference to it.
31176      * @type {Roo.View}
31177      */
31178     view : false,
31179     
31180       /**
31181      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31182      * <pre><code>
31183
31184 layout.addxtype({
31185        xtype : 'Form',
31186        items: [ .... ]
31187    }
31188 );
31189
31190 </code></pre>
31191      * @param {Object} cfg Xtype definition of item to add.
31192      */
31193     
31194     addxtype : function(cfg) {
31195         // add form..
31196         if (cfg.xtype.match(/^Form$/)) {
31197             
31198             var el;
31199             //if (this.footer) {
31200             //    el = this.footer.container.insertSibling(false, 'before');
31201             //} else {
31202                 el = this.el.createChild();
31203             //}
31204
31205             this.form = new  Roo.form.Form(cfg);
31206             
31207             
31208             if ( this.form.allItems.length) {
31209                 this.form.render(el.dom);
31210             }
31211             return this.form;
31212         }
31213         // should only have one of theses..
31214         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31215             // views.. should not be just added - used named prop 'view''
31216             
31217             cfg.el = this.el.appendChild(document.createElement("div"));
31218             // factory?
31219             
31220             var ret = new Roo.factory(cfg);
31221              
31222              ret.render && ret.render(false, ''); // render blank..
31223             this.view = ret;
31224             return ret;
31225         }
31226         return false;
31227     }
31228 });
31229
31230 /**
31231  * @class Roo.GridPanel
31232  * @extends Roo.ContentPanel
31233  * @constructor
31234  * Create a new GridPanel.
31235  * @param {Roo.grid.Grid} grid The grid for this panel
31236  * @param {String/Object} config A string to set only the panel's title, or a config object
31237  */
31238 Roo.GridPanel = function(grid, config){
31239     
31240   
31241     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31242         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31243         
31244     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31245     
31246     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31247     
31248     if(this.toolbar){
31249         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31250     }
31251     // xtype created footer. - not sure if will work as we normally have to render first..
31252     if (this.footer && !this.footer.el && this.footer.xtype) {
31253         
31254         this.footer.container = this.grid.getView().getFooterPanel(true);
31255         this.footer.dataSource = this.grid.dataSource;
31256         this.footer = Roo.factory(this.footer, Roo);
31257         
31258     }
31259     
31260     grid.monitorWindowResize = false; // turn off autosizing
31261     grid.autoHeight = false;
31262     grid.autoWidth = false;
31263     this.grid = grid;
31264     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31265 };
31266
31267 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31268     getId : function(){
31269         return this.grid.id;
31270     },
31271     
31272     /**
31273      * Returns the grid for this panel
31274      * @return {Roo.grid.Grid} 
31275      */
31276     getGrid : function(){
31277         return this.grid;    
31278     },
31279     
31280     setSize : function(width, height){
31281         if(!this.ignoreResize(width, height)){
31282             var grid = this.grid;
31283             var size = this.adjustForComponents(width, height);
31284             grid.getGridEl().setSize(size.width, size.height);
31285             grid.autoSize();
31286         }
31287     },
31288     
31289     beforeSlide : function(){
31290         this.grid.getView().scroller.clip();
31291     },
31292     
31293     afterSlide : function(){
31294         this.grid.getView().scroller.unclip();
31295     },
31296     
31297     destroy : function(){
31298         this.grid.destroy();
31299         delete this.grid;
31300         Roo.GridPanel.superclass.destroy.call(this); 
31301     }
31302 });
31303
31304
31305 /**
31306  * @class Roo.NestedLayoutPanel
31307  * @extends Roo.ContentPanel
31308  * @constructor
31309  * Create a new NestedLayoutPanel.
31310  * 
31311  * 
31312  * @param {Roo.BorderLayout} layout The layout for this panel
31313  * @param {String/Object} config A string to set only the title or a config object
31314  */
31315 Roo.NestedLayoutPanel = function(layout, config)
31316 {
31317     // construct with only one argument..
31318     /* FIXME - implement nicer consturctors
31319     if (layout.layout) {
31320         config = layout;
31321         layout = config.layout;
31322         delete config.layout;
31323     }
31324     if (layout.xtype && !layout.getEl) {
31325         // then layout needs constructing..
31326         layout = Roo.factory(layout, Roo);
31327     }
31328     */
31329     
31330     
31331     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31332     
31333     layout.monitorWindowResize = false; // turn off autosizing
31334     this.layout = layout;
31335     this.layout.getEl().addClass("x-layout-nested-layout");
31336     
31337     
31338     
31339     
31340 };
31341
31342 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31343
31344     setSize : function(width, height){
31345         if(!this.ignoreResize(width, height)){
31346             var size = this.adjustForComponents(width, height);
31347             var el = this.layout.getEl();
31348             el.setSize(size.width, size.height);
31349             var touch = el.dom.offsetWidth;
31350             this.layout.layout();
31351             // ie requires a double layout on the first pass
31352             if(Roo.isIE && !this.initialized){
31353                 this.initialized = true;
31354                 this.layout.layout();
31355             }
31356         }
31357     },
31358     
31359     // activate all subpanels if not currently active..
31360     
31361     setActiveState : function(active){
31362         this.active = active;
31363         if(!active){
31364             this.fireEvent("deactivate", this);
31365             return;
31366         }
31367         
31368         this.fireEvent("activate", this);
31369         // not sure if this should happen before or after..
31370         if (!this.layout) {
31371             return; // should not happen..
31372         }
31373         var reg = false;
31374         for (var r in this.layout.regions) {
31375             reg = this.layout.getRegion(r);
31376             if (reg.getActivePanel()) {
31377                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31378                 reg.setActivePanel(reg.getActivePanel());
31379                 continue;
31380             }
31381             if (!reg.panels.length) {
31382                 continue;
31383             }
31384             reg.showPanel(reg.getPanel(0));
31385         }
31386         
31387         
31388         
31389         
31390     },
31391     
31392     /**
31393      * Returns the nested BorderLayout for this panel
31394      * @return {Roo.BorderLayout} 
31395      */
31396     getLayout : function(){
31397         return this.layout;
31398     },
31399     
31400      /**
31401      * Adds a xtype elements to the layout of the nested panel
31402      * <pre><code>
31403
31404 panel.addxtype({
31405        xtype : 'ContentPanel',
31406        region: 'west',
31407        items: [ .... ]
31408    }
31409 );
31410
31411 panel.addxtype({
31412         xtype : 'NestedLayoutPanel',
31413         region: 'west',
31414         layout: {
31415            center: { },
31416            west: { }   
31417         },
31418         items : [ ... list of content panels or nested layout panels.. ]
31419    }
31420 );
31421 </code></pre>
31422      * @param {Object} cfg Xtype definition of item to add.
31423      */
31424     addxtype : function(cfg) {
31425         return this.layout.addxtype(cfg);
31426     
31427     }
31428 });
31429
31430 Roo.ScrollPanel = function(el, config, content){
31431     config = config || {};
31432     config.fitToFrame = true;
31433     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31434     
31435     this.el.dom.style.overflow = "hidden";
31436     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31437     this.el.removeClass("x-layout-inactive-content");
31438     this.el.on("mousewheel", this.onWheel, this);
31439
31440     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31441     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31442     up.unselectable(); down.unselectable();
31443     up.on("click", this.scrollUp, this);
31444     down.on("click", this.scrollDown, this);
31445     up.addClassOnOver("x-scroller-btn-over");
31446     down.addClassOnOver("x-scroller-btn-over");
31447     up.addClassOnClick("x-scroller-btn-click");
31448     down.addClassOnClick("x-scroller-btn-click");
31449     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31450
31451     this.resizeEl = this.el;
31452     this.el = wrap; this.up = up; this.down = down;
31453 };
31454
31455 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31456     increment : 100,
31457     wheelIncrement : 5,
31458     scrollUp : function(){
31459         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31460     },
31461
31462     scrollDown : function(){
31463         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31464     },
31465
31466     afterScroll : function(){
31467         var el = this.resizeEl;
31468         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31469         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31470         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31471     },
31472
31473     setSize : function(){
31474         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31475         this.afterScroll();
31476     },
31477
31478     onWheel : function(e){
31479         var d = e.getWheelDelta();
31480         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31481         this.afterScroll();
31482         e.stopEvent();
31483     },
31484
31485     setContent : function(content, loadScripts){
31486         this.resizeEl.update(content, loadScripts);
31487     }
31488
31489 });
31490
31491
31492
31493
31494
31495
31496
31497
31498
31499 /**
31500  * @class Roo.TreePanel
31501  * @extends Roo.ContentPanel
31502  * @constructor
31503  * Create a new TreePanel. - defaults to fit/scoll contents.
31504  * @param {String/Object} config A string to set only the panel's title, or a config object
31505  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31506  */
31507 Roo.TreePanel = function(config){
31508     var el = config.el;
31509     var tree = config.tree;
31510     delete config.tree; 
31511     delete config.el; // hopefull!
31512     
31513     // wrapper for IE7 strict & safari scroll issue
31514     
31515     var treeEl = el.createChild();
31516     config.resizeEl = treeEl;
31517     
31518     
31519     
31520     Roo.TreePanel.superclass.constructor.call(this, el, config);
31521  
31522  
31523     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31524     //console.log(tree);
31525     this.on('activate', function()
31526     {
31527         if (this.tree.rendered) {
31528             return;
31529         }
31530         //console.log('render tree');
31531         this.tree.render();
31532     });
31533     // this should not be needed.. - it's actually the 'el' that resizes?
31534     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
31535     
31536     //this.on('resize',  function (cp, w, h) {
31537     //        this.tree.innerCt.setWidth(w);
31538     //        this.tree.innerCt.setHeight(h);
31539     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
31540     //});
31541
31542         
31543     
31544 };
31545
31546 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31547     fitToFrame : true,
31548     autoScroll : true
31549 });
31550
31551
31552
31553
31554
31555
31556
31557
31558
31559
31560
31561 /*
31562  * Based on:
31563  * Ext JS Library 1.1.1
31564  * Copyright(c) 2006-2007, Ext JS, LLC.
31565  *
31566  * Originally Released Under LGPL - original licence link has changed is not relivant.
31567  *
31568  * Fork - LGPL
31569  * <script type="text/javascript">
31570  */
31571  
31572
31573 /**
31574  * @class Roo.ReaderLayout
31575  * @extends Roo.BorderLayout
31576  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31577  * center region containing two nested regions (a top one for a list view and one for item preview below),
31578  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31579  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31580  * expedites the setup of the overall layout and regions for this common application style.
31581  * Example:
31582  <pre><code>
31583 var reader = new Roo.ReaderLayout();
31584 var CP = Roo.ContentPanel;  // shortcut for adding
31585
31586 reader.beginUpdate();
31587 reader.add("north", new CP("north", "North"));
31588 reader.add("west", new CP("west", {title: "West"}));
31589 reader.add("east", new CP("east", {title: "East"}));
31590
31591 reader.regions.listView.add(new CP("listView", "List"));
31592 reader.regions.preview.add(new CP("preview", "Preview"));
31593 reader.endUpdate();
31594 </code></pre>
31595 * @constructor
31596 * Create a new ReaderLayout
31597 * @param {Object} config Configuration options
31598 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31599 * document.body if omitted)
31600 */
31601 Roo.ReaderLayout = function(config, renderTo){
31602     var c = config || {size:{}};
31603     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
31604         north: c.north !== false ? Roo.apply({
31605             split:false,
31606             initialSize: 32,
31607             titlebar: false
31608         }, c.north) : false,
31609         west: c.west !== false ? Roo.apply({
31610             split:true,
31611             initialSize: 200,
31612             minSize: 175,
31613             maxSize: 400,
31614             titlebar: true,
31615             collapsible: true,
31616             animate: true,
31617             margins:{left:5,right:0,bottom:5,top:5},
31618             cmargins:{left:5,right:5,bottom:5,top:5}
31619         }, c.west) : false,
31620         east: c.east !== false ? Roo.apply({
31621             split:true,
31622             initialSize: 200,
31623             minSize: 175,
31624             maxSize: 400,
31625             titlebar: true,
31626             collapsible: true,
31627             animate: true,
31628             margins:{left:0,right:5,bottom:5,top:5},
31629             cmargins:{left:5,right:5,bottom:5,top:5}
31630         }, c.east) : false,
31631         center: Roo.apply({
31632             tabPosition: 'top',
31633             autoScroll:false,
31634             closeOnTab: true,
31635             titlebar:false,
31636             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
31637         }, c.center)
31638     });
31639
31640     this.el.addClass('x-reader');
31641
31642     this.beginUpdate();
31643
31644     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
31645         south: c.preview !== false ? Roo.apply({
31646             split:true,
31647             initialSize: 200,
31648             minSize: 100,
31649             autoScroll:true,
31650             collapsible:true,
31651             titlebar: true,
31652             cmargins:{top:5,left:0, right:0, bottom:0}
31653         }, c.preview) : false,
31654         center: Roo.apply({
31655             autoScroll:false,
31656             titlebar:false,
31657             minHeight:200
31658         }, c.listView)
31659     });
31660     this.add('center', new Roo.NestedLayoutPanel(inner,
31661             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
31662
31663     this.endUpdate();
31664
31665     this.regions.preview = inner.getRegion('south');
31666     this.regions.listView = inner.getRegion('center');
31667 };
31668
31669 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
31670  * Based on:
31671  * Ext JS Library 1.1.1
31672  * Copyright(c) 2006-2007, Ext JS, LLC.
31673  *
31674  * Originally Released Under LGPL - original licence link has changed is not relivant.
31675  *
31676  * Fork - LGPL
31677  * <script type="text/javascript">
31678  */
31679  
31680 /**
31681  * @class Roo.grid.Grid
31682  * @extends Roo.util.Observable
31683  * This class represents the primary interface of a component based grid control.
31684  * <br><br>Usage:<pre><code>
31685  var grid = new Roo.grid.Grid("my-container-id", {
31686      ds: myDataStore,
31687      cm: myColModel,
31688      selModel: mySelectionModel,
31689      autoSizeColumns: true,
31690      monitorWindowResize: false,
31691      trackMouseOver: true
31692  });
31693  // set any options
31694  grid.render();
31695  * </code></pre>
31696  * <b>Common Problems:</b><br/>
31697  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
31698  * element will correct this<br/>
31699  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
31700  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
31701  * are unpredictable.<br/>
31702  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
31703  * grid to calculate dimensions/offsets.<br/>
31704   * @constructor
31705  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
31706  * The container MUST have some type of size defined for the grid to fill. The container will be
31707  * automatically set to position relative if it isn't already.
31708  * @param {Object} config A config object that sets properties on this grid.
31709  */
31710 Roo.grid.Grid = function(container, config){
31711         // initialize the container
31712         this.container = Roo.get(container);
31713         this.container.update("");
31714         this.container.setStyle("overflow", "hidden");
31715     this.container.addClass('x-grid-container');
31716
31717     this.id = this.container.id;
31718
31719     Roo.apply(this, config);
31720     // check and correct shorthanded configs
31721     if(this.ds){
31722         this.dataSource = this.ds;
31723         delete this.ds;
31724     }
31725     if(this.cm){
31726         this.colModel = this.cm;
31727         delete this.cm;
31728     }
31729     if(this.sm){
31730         this.selModel = this.sm;
31731         delete this.sm;
31732     }
31733
31734     if (this.selModel) {
31735         this.selModel = Roo.factory(this.selModel, Roo.grid);
31736         this.sm = this.selModel;
31737         this.sm.xmodule = this.xmodule || false;
31738     }
31739     if (typeof(this.colModel.config) == 'undefined') {
31740         this.colModel = new Roo.grid.ColumnModel(this.colModel);
31741         this.cm = this.colModel;
31742         this.cm.xmodule = this.xmodule || false;
31743     }
31744     if (this.dataSource) {
31745         this.dataSource= Roo.factory(this.dataSource, Roo.data);
31746         this.ds = this.dataSource;
31747         this.ds.xmodule = this.xmodule || false;
31748          
31749     }
31750     
31751     
31752     
31753     if(this.width){
31754         this.container.setWidth(this.width);
31755     }
31756
31757     if(this.height){
31758         this.container.setHeight(this.height);
31759     }
31760     /** @private */
31761         this.addEvents({
31762         // raw events
31763         /**
31764          * @event click
31765          * The raw click event for the entire grid.
31766          * @param {Roo.EventObject} e
31767          */
31768         "click" : true,
31769         /**
31770          * @event dblclick
31771          * The raw dblclick event for the entire grid.
31772          * @param {Roo.EventObject} e
31773          */
31774         "dblclick" : true,
31775         /**
31776          * @event contextmenu
31777          * The raw contextmenu event for the entire grid.
31778          * @param {Roo.EventObject} e
31779          */
31780         "contextmenu" : true,
31781         /**
31782          * @event mousedown
31783          * The raw mousedown event for the entire grid.
31784          * @param {Roo.EventObject} e
31785          */
31786         "mousedown" : true,
31787         /**
31788          * @event mouseup
31789          * The raw mouseup event for the entire grid.
31790          * @param {Roo.EventObject} e
31791          */
31792         "mouseup" : true,
31793         /**
31794          * @event mouseover
31795          * The raw mouseover event for the entire grid.
31796          * @param {Roo.EventObject} e
31797          */
31798         "mouseover" : true,
31799         /**
31800          * @event mouseout
31801          * The raw mouseout event for the entire grid.
31802          * @param {Roo.EventObject} e
31803          */
31804         "mouseout" : true,
31805         /**
31806          * @event keypress
31807          * The raw keypress event for the entire grid.
31808          * @param {Roo.EventObject} e
31809          */
31810         "keypress" : true,
31811         /**
31812          * @event keydown
31813          * The raw keydown event for the entire grid.
31814          * @param {Roo.EventObject} e
31815          */
31816         "keydown" : true,
31817
31818         // custom events
31819
31820         /**
31821          * @event cellclick
31822          * Fires when a cell is clicked
31823          * @param {Grid} this
31824          * @param {Number} rowIndex
31825          * @param {Number} columnIndex
31826          * @param {Roo.EventObject} e
31827          */
31828         "cellclick" : true,
31829         /**
31830          * @event celldblclick
31831          * Fires when a cell is double clicked
31832          * @param {Grid} this
31833          * @param {Number} rowIndex
31834          * @param {Number} columnIndex
31835          * @param {Roo.EventObject} e
31836          */
31837         "celldblclick" : true,
31838         /**
31839          * @event rowclick
31840          * Fires when a row is clicked
31841          * @param {Grid} this
31842          * @param {Number} rowIndex
31843          * @param {Roo.EventObject} e
31844          */
31845         "rowclick" : true,
31846         /**
31847          * @event rowdblclick
31848          * Fires when a row is double clicked
31849          * @param {Grid} this
31850          * @param {Number} rowIndex
31851          * @param {Roo.EventObject} e
31852          */
31853         "rowdblclick" : true,
31854         /**
31855          * @event headerclick
31856          * Fires when a header is clicked
31857          * @param {Grid} this
31858          * @param {Number} columnIndex
31859          * @param {Roo.EventObject} e
31860          */
31861         "headerclick" : true,
31862         /**
31863          * @event headerdblclick
31864          * Fires when a header cell is double clicked
31865          * @param {Grid} this
31866          * @param {Number} columnIndex
31867          * @param {Roo.EventObject} e
31868          */
31869         "headerdblclick" : true,
31870         /**
31871          * @event rowcontextmenu
31872          * Fires when a row is right clicked
31873          * @param {Grid} this
31874          * @param {Number} rowIndex
31875          * @param {Roo.EventObject} e
31876          */
31877         "rowcontextmenu" : true,
31878         /**
31879          * @event cellcontextmenu
31880          * Fires when a cell is right clicked
31881          * @param {Grid} this
31882          * @param {Number} rowIndex
31883          * @param {Number} cellIndex
31884          * @param {Roo.EventObject} e
31885          */
31886          "cellcontextmenu" : true,
31887         /**
31888          * @event headercontextmenu
31889          * Fires when a header is right clicked
31890          * @param {Grid} this
31891          * @param {Number} columnIndex
31892          * @param {Roo.EventObject} e
31893          */
31894         "headercontextmenu" : true,
31895         /**
31896          * @event bodyscroll
31897          * Fires when the body element is scrolled
31898          * @param {Number} scrollLeft
31899          * @param {Number} scrollTop
31900          */
31901         "bodyscroll" : true,
31902         /**
31903          * @event columnresize
31904          * Fires when the user resizes a column
31905          * @param {Number} columnIndex
31906          * @param {Number} newSize
31907          */
31908         "columnresize" : true,
31909         /**
31910          * @event columnmove
31911          * Fires when the user moves a column
31912          * @param {Number} oldIndex
31913          * @param {Number} newIndex
31914          */
31915         "columnmove" : true,
31916         /**
31917          * @event startdrag
31918          * Fires when row(s) start being dragged
31919          * @param {Grid} this
31920          * @param {Roo.GridDD} dd The drag drop object
31921          * @param {event} e The raw browser event
31922          */
31923         "startdrag" : true,
31924         /**
31925          * @event enddrag
31926          * Fires when a drag operation is complete
31927          * @param {Grid} this
31928          * @param {Roo.GridDD} dd The drag drop object
31929          * @param {event} e The raw browser event
31930          */
31931         "enddrag" : true,
31932         /**
31933          * @event dragdrop
31934          * Fires when dragged row(s) are dropped on a valid DD target
31935          * @param {Grid} this
31936          * @param {Roo.GridDD} dd The drag drop object
31937          * @param {String} targetId The target drag drop object
31938          * @param {event} e The raw browser event
31939          */
31940         "dragdrop" : true,
31941         /**
31942          * @event dragover
31943          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
31944          * @param {Grid} this
31945          * @param {Roo.GridDD} dd The drag drop object
31946          * @param {String} targetId The target drag drop object
31947          * @param {event} e The raw browser event
31948          */
31949         "dragover" : true,
31950         /**
31951          * @event dragenter
31952          *  Fires when the dragged row(s) first cross another DD target while being dragged
31953          * @param {Grid} this
31954          * @param {Roo.GridDD} dd The drag drop object
31955          * @param {String} targetId The target drag drop object
31956          * @param {event} e The raw browser event
31957          */
31958         "dragenter" : true,
31959         /**
31960          * @event dragout
31961          * Fires when the dragged row(s) leave another DD target while being dragged
31962          * @param {Grid} this
31963          * @param {Roo.GridDD} dd The drag drop object
31964          * @param {String} targetId The target drag drop object
31965          * @param {event} e The raw browser event
31966          */
31967         "dragout" : true,
31968         /**
31969          * @event rowclass
31970          * Fires when a row is rendered, so you can change add a style to it.
31971          * @param {GridView} gridview   The grid view
31972          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
31973          */
31974         'rowclass' : true,
31975
31976         /**
31977          * @event render
31978          * Fires when the grid is rendered
31979          * @param {Grid} grid
31980          */
31981         'render' : true
31982     });
31983
31984     Roo.grid.Grid.superclass.constructor.call(this);
31985 };
31986 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
31987     
31988     /**
31989      * @cfg {String} ddGroup - drag drop group.
31990      */
31991
31992     /**
31993      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
31994      */
31995     minColumnWidth : 25,
31996
31997     /**
31998      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
31999      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32000      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32001      */
32002     autoSizeColumns : false,
32003
32004     /**
32005      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32006      */
32007     autoSizeHeaders : true,
32008
32009     /**
32010      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32011      */
32012     monitorWindowResize : true,
32013
32014     /**
32015      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32016      * rows measured to get a columns size. Default is 0 (all rows).
32017      */
32018     maxRowsToMeasure : 0,
32019
32020     /**
32021      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32022      */
32023     trackMouseOver : true,
32024
32025     /**
32026     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32027     */
32028     
32029     /**
32030     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32031     */
32032     enableDragDrop : false,
32033     
32034     /**
32035     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32036     */
32037     enableColumnMove : true,
32038     
32039     /**
32040     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32041     */
32042     enableColumnHide : true,
32043     
32044     /**
32045     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32046     */
32047     enableRowHeightSync : false,
32048     
32049     /**
32050     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32051     */
32052     stripeRows : true,
32053     
32054     /**
32055     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32056     */
32057     autoHeight : false,
32058
32059     /**
32060      * @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.
32061      */
32062     autoExpandColumn : false,
32063
32064     /**
32065     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32066     * Default is 50.
32067     */
32068     autoExpandMin : 50,
32069
32070     /**
32071     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32072     */
32073     autoExpandMax : 1000,
32074
32075     /**
32076     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32077     */
32078     view : null,
32079
32080     /**
32081     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32082     */
32083     loadMask : false,
32084     /**
32085     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32086     */
32087     dropTarget: false,
32088     
32089    
32090     
32091     // private
32092     rendered : false,
32093
32094     /**
32095     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32096     * of a fixed width. Default is false.
32097     */
32098     /**
32099     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32100     */
32101     /**
32102      * Called once after all setup has been completed and the grid is ready to be rendered.
32103      * @return {Roo.grid.Grid} this
32104      */
32105     render : function()
32106     {
32107         var c = this.container;
32108         // try to detect autoHeight/width mode
32109         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32110             this.autoHeight = true;
32111         }
32112         var view = this.getView();
32113         view.init(this);
32114
32115         c.on("click", this.onClick, this);
32116         c.on("dblclick", this.onDblClick, this);
32117         c.on("contextmenu", this.onContextMenu, this);
32118         c.on("keydown", this.onKeyDown, this);
32119         if (Roo.isTouch) {
32120             c.on("touchstart", this.onTouchStart, this);
32121         }
32122
32123         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32124
32125         this.getSelectionModel().init(this);
32126
32127         view.render();
32128
32129         if(this.loadMask){
32130             this.loadMask = new Roo.LoadMask(this.container,
32131                     Roo.apply({store:this.dataSource}, this.loadMask));
32132         }
32133         
32134         
32135         if (this.toolbar && this.toolbar.xtype) {
32136             this.toolbar.container = this.getView().getHeaderPanel(true);
32137             this.toolbar = new Roo.Toolbar(this.toolbar);
32138         }
32139         if (this.footer && this.footer.xtype) {
32140             this.footer.dataSource = this.getDataSource();
32141             this.footer.container = this.getView().getFooterPanel(true);
32142             this.footer = Roo.factory(this.footer, Roo);
32143         }
32144         if (this.dropTarget && this.dropTarget.xtype) {
32145             delete this.dropTarget.xtype;
32146             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32147         }
32148         
32149         
32150         this.rendered = true;
32151         this.fireEvent('render', this);
32152         return this;
32153     },
32154
32155         /**
32156          * Reconfigures the grid to use a different Store and Column Model.
32157          * The View will be bound to the new objects and refreshed.
32158          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32159          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32160          */
32161     reconfigure : function(dataSource, colModel){
32162         if(this.loadMask){
32163             this.loadMask.destroy();
32164             this.loadMask = new Roo.LoadMask(this.container,
32165                     Roo.apply({store:dataSource}, this.loadMask));
32166         }
32167         this.view.bind(dataSource, colModel);
32168         this.dataSource = dataSource;
32169         this.colModel = colModel;
32170         this.view.refresh(true);
32171     },
32172
32173     // private
32174     onKeyDown : function(e){
32175         this.fireEvent("keydown", e);
32176     },
32177
32178     /**
32179      * Destroy this grid.
32180      * @param {Boolean} removeEl True to remove the element
32181      */
32182     destroy : function(removeEl, keepListeners){
32183         if(this.loadMask){
32184             this.loadMask.destroy();
32185         }
32186         var c = this.container;
32187         c.removeAllListeners();
32188         this.view.destroy();
32189         this.colModel.purgeListeners();
32190         if(!keepListeners){
32191             this.purgeListeners();
32192         }
32193         c.update("");
32194         if(removeEl === true){
32195             c.remove();
32196         }
32197     },
32198
32199     // private
32200     processEvent : function(name, e){
32201         // does this fire select???
32202         //Roo.log('grid:processEvent '  + name);
32203         
32204         if (name != 'touchstart' ) {
32205             this.fireEvent(name, e);    
32206         }
32207         
32208         var t = e.getTarget();
32209         var v = this.view;
32210         var header = v.findHeaderIndex(t);
32211         if(header !== false){
32212             var ename = name == 'touchstart' ? 'click' : name;
32213              
32214             this.fireEvent("header" + ename, this, header, e);
32215         }else{
32216             var row = v.findRowIndex(t);
32217             var cell = v.findCellIndex(t);
32218             if (name == 'touchstart') {
32219                 // first touch is always a click.
32220                 // hopefull this happens after selection is updated.?
32221                 name = false;
32222                 
32223                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32224                     var cs = this.selModel.getSelectedCell();
32225                     if (row == cs[0] && cell == cs[1]){
32226                         name = 'dblclick';
32227                     }
32228                 }
32229                 if (typeof(this.selModel.getSelections) != 'undefined') {
32230                     var cs = this.selModel.getSelections();
32231                     var ds = this.dataSource;
32232                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
32233                         name = 'dblclick';
32234                     }
32235                 }
32236                 if (!name) {
32237                     return;
32238                 }
32239             }
32240             
32241             
32242             if(row !== false){
32243                 this.fireEvent("row" + name, this, row, e);
32244                 if(cell !== false){
32245                     this.fireEvent("cell" + name, this, row, cell, e);
32246                 }
32247             }
32248         }
32249     },
32250
32251     // private
32252     onClick : function(e){
32253         this.processEvent("click", e);
32254     },
32255    // private
32256     onTouchStart : function(e){
32257         this.processEvent("touchstart", e);
32258     },
32259
32260     // private
32261     onContextMenu : function(e, t){
32262         this.processEvent("contextmenu", e);
32263     },
32264
32265     // private
32266     onDblClick : function(e){
32267         this.processEvent("dblclick", e);
32268     },
32269
32270     // private
32271     walkCells : function(row, col, step, fn, scope){
32272         var cm = this.colModel, clen = cm.getColumnCount();
32273         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32274         if(step < 0){
32275             if(col < 0){
32276                 row--;
32277                 first = false;
32278             }
32279             while(row >= 0){
32280                 if(!first){
32281                     col = clen-1;
32282                 }
32283                 first = false;
32284                 while(col >= 0){
32285                     if(fn.call(scope || this, row, col, cm) === true){
32286                         return [row, col];
32287                     }
32288                     col--;
32289                 }
32290                 row--;
32291             }
32292         } else {
32293             if(col >= clen){
32294                 row++;
32295                 first = false;
32296             }
32297             while(row < rlen){
32298                 if(!first){
32299                     col = 0;
32300                 }
32301                 first = false;
32302                 while(col < clen){
32303                     if(fn.call(scope || this, row, col, cm) === true){
32304                         return [row, col];
32305                     }
32306                     col++;
32307                 }
32308                 row++;
32309             }
32310         }
32311         return null;
32312     },
32313
32314     // private
32315     getSelections : function(){
32316         return this.selModel.getSelections();
32317     },
32318
32319     /**
32320      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32321      * but if manual update is required this method will initiate it.
32322      */
32323     autoSize : function(){
32324         if(this.rendered){
32325             this.view.layout();
32326             if(this.view.adjustForScroll){
32327                 this.view.adjustForScroll();
32328             }
32329         }
32330     },
32331
32332     /**
32333      * Returns the grid's underlying element.
32334      * @return {Element} The element
32335      */
32336     getGridEl : function(){
32337         return this.container;
32338     },
32339
32340     // private for compatibility, overridden by editor grid
32341     stopEditing : function(){},
32342
32343     /**
32344      * Returns the grid's SelectionModel.
32345      * @return {SelectionModel}
32346      */
32347     getSelectionModel : function(){
32348         if(!this.selModel){
32349             this.selModel = new Roo.grid.RowSelectionModel();
32350         }
32351         return this.selModel;
32352     },
32353
32354     /**
32355      * Returns the grid's DataSource.
32356      * @return {DataSource}
32357      */
32358     getDataSource : function(){
32359         return this.dataSource;
32360     },
32361
32362     /**
32363      * Returns the grid's ColumnModel.
32364      * @return {ColumnModel}
32365      */
32366     getColumnModel : function(){
32367         return this.colModel;
32368     },
32369
32370     /**
32371      * Returns the grid's GridView object.
32372      * @return {GridView}
32373      */
32374     getView : function(){
32375         if(!this.view){
32376             this.view = new Roo.grid.GridView(this.viewConfig);
32377         }
32378         return this.view;
32379     },
32380     /**
32381      * Called to get grid's drag proxy text, by default returns this.ddText.
32382      * @return {String}
32383      */
32384     getDragDropText : function(){
32385         var count = this.selModel.getCount();
32386         return String.format(this.ddText, count, count == 1 ? '' : 's');
32387     }
32388 });
32389 /**
32390  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32391  * %0 is replaced with the number of selected rows.
32392  * @type String
32393  */
32394 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32395  * Based on:
32396  * Ext JS Library 1.1.1
32397  * Copyright(c) 2006-2007, Ext JS, LLC.
32398  *
32399  * Originally Released Under LGPL - original licence link has changed is not relivant.
32400  *
32401  * Fork - LGPL
32402  * <script type="text/javascript">
32403  */
32404  
32405 Roo.grid.AbstractGridView = function(){
32406         this.grid = null;
32407         
32408         this.events = {
32409             "beforerowremoved" : true,
32410             "beforerowsinserted" : true,
32411             "beforerefresh" : true,
32412             "rowremoved" : true,
32413             "rowsinserted" : true,
32414             "rowupdated" : true,
32415             "refresh" : true
32416         };
32417     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32418 };
32419
32420 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32421     rowClass : "x-grid-row",
32422     cellClass : "x-grid-cell",
32423     tdClass : "x-grid-td",
32424     hdClass : "x-grid-hd",
32425     splitClass : "x-grid-hd-split",
32426     
32427     init: function(grid){
32428         this.grid = grid;
32429                 var cid = this.grid.getGridEl().id;
32430         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32431         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32432         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32433         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32434         },
32435         
32436     getColumnRenderers : function(){
32437         var renderers = [];
32438         var cm = this.grid.colModel;
32439         var colCount = cm.getColumnCount();
32440         for(var i = 0; i < colCount; i++){
32441             renderers[i] = cm.getRenderer(i);
32442         }
32443         return renderers;
32444     },
32445     
32446     getColumnIds : function(){
32447         var ids = [];
32448         var cm = this.grid.colModel;
32449         var colCount = cm.getColumnCount();
32450         for(var i = 0; i < colCount; i++){
32451             ids[i] = cm.getColumnId(i);
32452         }
32453         return ids;
32454     },
32455     
32456     getDataIndexes : function(){
32457         if(!this.indexMap){
32458             this.indexMap = this.buildIndexMap();
32459         }
32460         return this.indexMap.colToData;
32461     },
32462     
32463     getColumnIndexByDataIndex : function(dataIndex){
32464         if(!this.indexMap){
32465             this.indexMap = this.buildIndexMap();
32466         }
32467         return this.indexMap.dataToCol[dataIndex];
32468     },
32469     
32470     /**
32471      * Set a css style for a column dynamically. 
32472      * @param {Number} colIndex The index of the column
32473      * @param {String} name The css property name
32474      * @param {String} value The css value
32475      */
32476     setCSSStyle : function(colIndex, name, value){
32477         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32478         Roo.util.CSS.updateRule(selector, name, value);
32479     },
32480     
32481     generateRules : function(cm){
32482         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32483         Roo.util.CSS.removeStyleSheet(rulesId);
32484         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32485             var cid = cm.getColumnId(i);
32486             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32487                          this.tdSelector, cid, " {\n}\n",
32488                          this.hdSelector, cid, " {\n}\n",
32489                          this.splitSelector, cid, " {\n}\n");
32490         }
32491         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32492     }
32493 });/*
32494  * Based on:
32495  * Ext JS Library 1.1.1
32496  * Copyright(c) 2006-2007, Ext JS, LLC.
32497  *
32498  * Originally Released Under LGPL - original licence link has changed is not relivant.
32499  *
32500  * Fork - LGPL
32501  * <script type="text/javascript">
32502  */
32503
32504 // private
32505 // This is a support class used internally by the Grid components
32506 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32507     this.grid = grid;
32508     this.view = grid.getView();
32509     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32510     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32511     if(hd2){
32512         this.setHandleElId(Roo.id(hd));
32513         this.setOuterHandleElId(Roo.id(hd2));
32514     }
32515     this.scroll = false;
32516 };
32517 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32518     maxDragWidth: 120,
32519     getDragData : function(e){
32520         var t = Roo.lib.Event.getTarget(e);
32521         var h = this.view.findHeaderCell(t);
32522         if(h){
32523             return {ddel: h.firstChild, header:h};
32524         }
32525         return false;
32526     },
32527
32528     onInitDrag : function(e){
32529         this.view.headersDisabled = true;
32530         var clone = this.dragData.ddel.cloneNode(true);
32531         clone.id = Roo.id();
32532         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32533         this.proxy.update(clone);
32534         return true;
32535     },
32536
32537     afterValidDrop : function(){
32538         var v = this.view;
32539         setTimeout(function(){
32540             v.headersDisabled = false;
32541         }, 50);
32542     },
32543
32544     afterInvalidDrop : function(){
32545         var v = this.view;
32546         setTimeout(function(){
32547             v.headersDisabled = false;
32548         }, 50);
32549     }
32550 });
32551 /*
32552  * Based on:
32553  * Ext JS Library 1.1.1
32554  * Copyright(c) 2006-2007, Ext JS, LLC.
32555  *
32556  * Originally Released Under LGPL - original licence link has changed is not relivant.
32557  *
32558  * Fork - LGPL
32559  * <script type="text/javascript">
32560  */
32561 // private
32562 // This is a support class used internally by the Grid components
32563 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32564     this.grid = grid;
32565     this.view = grid.getView();
32566     // split the proxies so they don't interfere with mouse events
32567     this.proxyTop = Roo.DomHelper.append(document.body, {
32568         cls:"col-move-top", html:"&#160;"
32569     }, true);
32570     this.proxyBottom = Roo.DomHelper.append(document.body, {
32571         cls:"col-move-bottom", html:"&#160;"
32572     }, true);
32573     this.proxyTop.hide = this.proxyBottom.hide = function(){
32574         this.setLeftTop(-100,-100);
32575         this.setStyle("visibility", "hidden");
32576     };
32577     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32578     // temporarily disabled
32579     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
32580     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
32581 };
32582 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
32583     proxyOffsets : [-4, -9],
32584     fly: Roo.Element.fly,
32585
32586     getTargetFromEvent : function(e){
32587         var t = Roo.lib.Event.getTarget(e);
32588         var cindex = this.view.findCellIndex(t);
32589         if(cindex !== false){
32590             return this.view.getHeaderCell(cindex);
32591         }
32592         return null;
32593     },
32594
32595     nextVisible : function(h){
32596         var v = this.view, cm = this.grid.colModel;
32597         h = h.nextSibling;
32598         while(h){
32599             if(!cm.isHidden(v.getCellIndex(h))){
32600                 return h;
32601             }
32602             h = h.nextSibling;
32603         }
32604         return null;
32605     },
32606
32607     prevVisible : function(h){
32608         var v = this.view, cm = this.grid.colModel;
32609         h = h.prevSibling;
32610         while(h){
32611             if(!cm.isHidden(v.getCellIndex(h))){
32612                 return h;
32613             }
32614             h = h.prevSibling;
32615         }
32616         return null;
32617     },
32618
32619     positionIndicator : function(h, n, e){
32620         var x = Roo.lib.Event.getPageX(e);
32621         var r = Roo.lib.Dom.getRegion(n.firstChild);
32622         var px, pt, py = r.top + this.proxyOffsets[1];
32623         if((r.right - x) <= (r.right-r.left)/2){
32624             px = r.right+this.view.borderWidth;
32625             pt = "after";
32626         }else{
32627             px = r.left;
32628             pt = "before";
32629         }
32630         var oldIndex = this.view.getCellIndex(h);
32631         var newIndex = this.view.getCellIndex(n);
32632
32633         if(this.grid.colModel.isFixed(newIndex)){
32634             return false;
32635         }
32636
32637         var locked = this.grid.colModel.isLocked(newIndex);
32638
32639         if(pt == "after"){
32640             newIndex++;
32641         }
32642         if(oldIndex < newIndex){
32643             newIndex--;
32644         }
32645         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
32646             return false;
32647         }
32648         px +=  this.proxyOffsets[0];
32649         this.proxyTop.setLeftTop(px, py);
32650         this.proxyTop.show();
32651         if(!this.bottomOffset){
32652             this.bottomOffset = this.view.mainHd.getHeight();
32653         }
32654         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
32655         this.proxyBottom.show();
32656         return pt;
32657     },
32658
32659     onNodeEnter : function(n, dd, e, data){
32660         if(data.header != n){
32661             this.positionIndicator(data.header, n, e);
32662         }
32663     },
32664
32665     onNodeOver : function(n, dd, e, data){
32666         var result = false;
32667         if(data.header != n){
32668             result = this.positionIndicator(data.header, n, e);
32669         }
32670         if(!result){
32671             this.proxyTop.hide();
32672             this.proxyBottom.hide();
32673         }
32674         return result ? this.dropAllowed : this.dropNotAllowed;
32675     },
32676
32677     onNodeOut : function(n, dd, e, data){
32678         this.proxyTop.hide();
32679         this.proxyBottom.hide();
32680     },
32681
32682     onNodeDrop : function(n, dd, e, data){
32683         var h = data.header;
32684         if(h != n){
32685             var cm = this.grid.colModel;
32686             var x = Roo.lib.Event.getPageX(e);
32687             var r = Roo.lib.Dom.getRegion(n.firstChild);
32688             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
32689             var oldIndex = this.view.getCellIndex(h);
32690             var newIndex = this.view.getCellIndex(n);
32691             var locked = cm.isLocked(newIndex);
32692             if(pt == "after"){
32693                 newIndex++;
32694             }
32695             if(oldIndex < newIndex){
32696                 newIndex--;
32697             }
32698             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
32699                 return false;
32700             }
32701             cm.setLocked(oldIndex, locked, true);
32702             cm.moveColumn(oldIndex, newIndex);
32703             this.grid.fireEvent("columnmove", oldIndex, newIndex);
32704             return true;
32705         }
32706         return false;
32707     }
32708 });
32709 /*
32710  * Based on:
32711  * Ext JS Library 1.1.1
32712  * Copyright(c) 2006-2007, Ext JS, LLC.
32713  *
32714  * Originally Released Under LGPL - original licence link has changed is not relivant.
32715  *
32716  * Fork - LGPL
32717  * <script type="text/javascript">
32718  */
32719   
32720 /**
32721  * @class Roo.grid.GridView
32722  * @extends Roo.util.Observable
32723  *
32724  * @constructor
32725  * @param {Object} config
32726  */
32727 Roo.grid.GridView = function(config){
32728     Roo.grid.GridView.superclass.constructor.call(this);
32729     this.el = null;
32730
32731     Roo.apply(this, config);
32732 };
32733
32734 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
32735
32736     unselectable :  'unselectable="on"',
32737     unselectableCls :  'x-unselectable',
32738     
32739     
32740     rowClass : "x-grid-row",
32741
32742     cellClass : "x-grid-col",
32743
32744     tdClass : "x-grid-td",
32745
32746     hdClass : "x-grid-hd",
32747
32748     splitClass : "x-grid-split",
32749
32750     sortClasses : ["sort-asc", "sort-desc"],
32751
32752     enableMoveAnim : false,
32753
32754     hlColor: "C3DAF9",
32755
32756     dh : Roo.DomHelper,
32757
32758     fly : Roo.Element.fly,
32759
32760     css : Roo.util.CSS,
32761
32762     borderWidth: 1,
32763
32764     splitOffset: 3,
32765
32766     scrollIncrement : 22,
32767
32768     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
32769
32770     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
32771
32772     bind : function(ds, cm){
32773         if(this.ds){
32774             this.ds.un("load", this.onLoad, this);
32775             this.ds.un("datachanged", this.onDataChange, this);
32776             this.ds.un("add", this.onAdd, this);
32777             this.ds.un("remove", this.onRemove, this);
32778             this.ds.un("update", this.onUpdate, this);
32779             this.ds.un("clear", this.onClear, this);
32780         }
32781         if(ds){
32782             ds.on("load", this.onLoad, this);
32783             ds.on("datachanged", this.onDataChange, this);
32784             ds.on("add", this.onAdd, this);
32785             ds.on("remove", this.onRemove, this);
32786             ds.on("update", this.onUpdate, this);
32787             ds.on("clear", this.onClear, this);
32788         }
32789         this.ds = ds;
32790
32791         if(this.cm){
32792             this.cm.un("widthchange", this.onColWidthChange, this);
32793             this.cm.un("headerchange", this.onHeaderChange, this);
32794             this.cm.un("hiddenchange", this.onHiddenChange, this);
32795             this.cm.un("columnmoved", this.onColumnMove, this);
32796             this.cm.un("columnlockchange", this.onColumnLock, this);
32797         }
32798         if(cm){
32799             this.generateRules(cm);
32800             cm.on("widthchange", this.onColWidthChange, this);
32801             cm.on("headerchange", this.onHeaderChange, this);
32802             cm.on("hiddenchange", this.onHiddenChange, this);
32803             cm.on("columnmoved", this.onColumnMove, this);
32804             cm.on("columnlockchange", this.onColumnLock, this);
32805         }
32806         this.cm = cm;
32807     },
32808
32809     init: function(grid){
32810         Roo.grid.GridView.superclass.init.call(this, grid);
32811
32812         this.bind(grid.dataSource, grid.colModel);
32813
32814         grid.on("headerclick", this.handleHeaderClick, this);
32815
32816         if(grid.trackMouseOver){
32817             grid.on("mouseover", this.onRowOver, this);
32818             grid.on("mouseout", this.onRowOut, this);
32819         }
32820         grid.cancelTextSelection = function(){};
32821         this.gridId = grid.id;
32822
32823         var tpls = this.templates || {};
32824
32825         if(!tpls.master){
32826             tpls.master = new Roo.Template(
32827                '<div class="x-grid" hidefocus="true">',
32828                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
32829                   '<div class="x-grid-topbar"></div>',
32830                   '<div class="x-grid-scroller"><div></div></div>',
32831                   '<div class="x-grid-locked">',
32832                       '<div class="x-grid-header">{lockedHeader}</div>',
32833                       '<div class="x-grid-body">{lockedBody}</div>',
32834                   "</div>",
32835                   '<div class="x-grid-viewport">',
32836                       '<div class="x-grid-header">{header}</div>',
32837                       '<div class="x-grid-body">{body}</div>',
32838                   "</div>",
32839                   '<div class="x-grid-bottombar"></div>',
32840                  
32841                   '<div class="x-grid-resize-proxy">&#160;</div>',
32842                "</div>"
32843             );
32844             tpls.master.disableformats = true;
32845         }
32846
32847         if(!tpls.header){
32848             tpls.header = new Roo.Template(
32849                '<table border="0" cellspacing="0" cellpadding="0">',
32850                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
32851                "</table>{splits}"
32852             );
32853             tpls.header.disableformats = true;
32854         }
32855         tpls.header.compile();
32856
32857         if(!tpls.hcell){
32858             tpls.hcell = new Roo.Template(
32859                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
32860                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
32861                 "</div></td>"
32862              );
32863              tpls.hcell.disableFormats = true;
32864         }
32865         tpls.hcell.compile();
32866
32867         if(!tpls.hsplit){
32868             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
32869                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
32870             tpls.hsplit.disableFormats = true;
32871         }
32872         tpls.hsplit.compile();
32873
32874         if(!tpls.body){
32875             tpls.body = new Roo.Template(
32876                '<table border="0" cellspacing="0" cellpadding="0">',
32877                "<tbody>{rows}</tbody>",
32878                "</table>"
32879             );
32880             tpls.body.disableFormats = true;
32881         }
32882         tpls.body.compile();
32883
32884         if(!tpls.row){
32885             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
32886             tpls.row.disableFormats = true;
32887         }
32888         tpls.row.compile();
32889
32890         if(!tpls.cell){
32891             tpls.cell = new Roo.Template(
32892                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
32893                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
32894                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
32895                 "</td>"
32896             );
32897             tpls.cell.disableFormats = true;
32898         }
32899         tpls.cell.compile();
32900
32901         this.templates = tpls;
32902     },
32903
32904     // remap these for backwards compat
32905     onColWidthChange : function(){
32906         this.updateColumns.apply(this, arguments);
32907     },
32908     onHeaderChange : function(){
32909         this.updateHeaders.apply(this, arguments);
32910     }, 
32911     onHiddenChange : function(){
32912         this.handleHiddenChange.apply(this, arguments);
32913     },
32914     onColumnMove : function(){
32915         this.handleColumnMove.apply(this, arguments);
32916     },
32917     onColumnLock : function(){
32918         this.handleLockChange.apply(this, arguments);
32919     },
32920
32921     onDataChange : function(){
32922         this.refresh();
32923         this.updateHeaderSortState();
32924     },
32925
32926     onClear : function(){
32927         this.refresh();
32928     },
32929
32930     onUpdate : function(ds, record){
32931         this.refreshRow(record);
32932     },
32933
32934     refreshRow : function(record){
32935         var ds = this.ds, index;
32936         if(typeof record == 'number'){
32937             index = record;
32938             record = ds.getAt(index);
32939         }else{
32940             index = ds.indexOf(record);
32941         }
32942         this.insertRows(ds, index, index, true);
32943         this.onRemove(ds, record, index+1, true);
32944         this.syncRowHeights(index, index);
32945         this.layout();
32946         this.fireEvent("rowupdated", this, index, record);
32947     },
32948
32949     onAdd : function(ds, records, index){
32950         this.insertRows(ds, index, index + (records.length-1));
32951     },
32952
32953     onRemove : function(ds, record, index, isUpdate){
32954         if(isUpdate !== true){
32955             this.fireEvent("beforerowremoved", this, index, record);
32956         }
32957         var bt = this.getBodyTable(), lt = this.getLockedTable();
32958         if(bt.rows[index]){
32959             bt.firstChild.removeChild(bt.rows[index]);
32960         }
32961         if(lt.rows[index]){
32962             lt.firstChild.removeChild(lt.rows[index]);
32963         }
32964         if(isUpdate !== true){
32965             this.stripeRows(index);
32966             this.syncRowHeights(index, index);
32967             this.layout();
32968             this.fireEvent("rowremoved", this, index, record);
32969         }
32970     },
32971
32972     onLoad : function(){
32973         this.scrollToTop();
32974     },
32975
32976     /**
32977      * Scrolls the grid to the top
32978      */
32979     scrollToTop : function(){
32980         if(this.scroller){
32981             this.scroller.dom.scrollTop = 0;
32982             this.syncScroll();
32983         }
32984     },
32985
32986     /**
32987      * Gets a panel in the header of the grid that can be used for toolbars etc.
32988      * After modifying the contents of this panel a call to grid.autoSize() may be
32989      * required to register any changes in size.
32990      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
32991      * @return Roo.Element
32992      */
32993     getHeaderPanel : function(doShow){
32994         if(doShow){
32995             this.headerPanel.show();
32996         }
32997         return this.headerPanel;
32998     },
32999
33000     /**
33001      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33002      * After modifying the contents of this panel a call to grid.autoSize() may be
33003      * required to register any changes in size.
33004      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33005      * @return Roo.Element
33006      */
33007     getFooterPanel : function(doShow){
33008         if(doShow){
33009             this.footerPanel.show();
33010         }
33011         return this.footerPanel;
33012     },
33013
33014     initElements : function(){
33015         var E = Roo.Element;
33016         var el = this.grid.getGridEl().dom.firstChild;
33017         var cs = el.childNodes;
33018
33019         this.el = new E(el);
33020         
33021          this.focusEl = new E(el.firstChild);
33022         this.focusEl.swallowEvent("click", true);
33023         
33024         this.headerPanel = new E(cs[1]);
33025         this.headerPanel.enableDisplayMode("block");
33026
33027         this.scroller = new E(cs[2]);
33028         this.scrollSizer = new E(this.scroller.dom.firstChild);
33029
33030         this.lockedWrap = new E(cs[3]);
33031         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33032         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33033
33034         this.mainWrap = new E(cs[4]);
33035         this.mainHd = new E(this.mainWrap.dom.firstChild);
33036         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33037
33038         this.footerPanel = new E(cs[5]);
33039         this.footerPanel.enableDisplayMode("block");
33040
33041         this.resizeProxy = new E(cs[6]);
33042
33043         this.headerSelector = String.format(
33044            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33045            this.lockedHd.id, this.mainHd.id
33046         );
33047
33048         this.splitterSelector = String.format(
33049            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33050            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33051         );
33052     },
33053     idToCssName : function(s)
33054     {
33055         return s.replace(/[^a-z0-9]+/ig, '-');
33056     },
33057
33058     getHeaderCell : function(index){
33059         return Roo.DomQuery.select(this.headerSelector)[index];
33060     },
33061
33062     getHeaderCellMeasure : function(index){
33063         return this.getHeaderCell(index).firstChild;
33064     },
33065
33066     getHeaderCellText : function(index){
33067         return this.getHeaderCell(index).firstChild.firstChild;
33068     },
33069
33070     getLockedTable : function(){
33071         return this.lockedBody.dom.firstChild;
33072     },
33073
33074     getBodyTable : function(){
33075         return this.mainBody.dom.firstChild;
33076     },
33077
33078     getLockedRow : function(index){
33079         return this.getLockedTable().rows[index];
33080     },
33081
33082     getRow : function(index){
33083         return this.getBodyTable().rows[index];
33084     },
33085
33086     getRowComposite : function(index){
33087         if(!this.rowEl){
33088             this.rowEl = new Roo.CompositeElementLite();
33089         }
33090         var els = [], lrow, mrow;
33091         if(lrow = this.getLockedRow(index)){
33092             els.push(lrow);
33093         }
33094         if(mrow = this.getRow(index)){
33095             els.push(mrow);
33096         }
33097         this.rowEl.elements = els;
33098         return this.rowEl;
33099     },
33100     /**
33101      * Gets the 'td' of the cell
33102      * 
33103      * @param {Integer} rowIndex row to select
33104      * @param {Integer} colIndex column to select
33105      * 
33106      * @return {Object} 
33107      */
33108     getCell : function(rowIndex, colIndex){
33109         var locked = this.cm.getLockedCount();
33110         var source;
33111         if(colIndex < locked){
33112             source = this.lockedBody.dom.firstChild;
33113         }else{
33114             source = this.mainBody.dom.firstChild;
33115             colIndex -= locked;
33116         }
33117         return source.rows[rowIndex].childNodes[colIndex];
33118     },
33119
33120     getCellText : function(rowIndex, colIndex){
33121         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33122     },
33123
33124     getCellBox : function(cell){
33125         var b = this.fly(cell).getBox();
33126         if(Roo.isOpera){ // opera fails to report the Y
33127             b.y = cell.offsetTop + this.mainBody.getY();
33128         }
33129         return b;
33130     },
33131
33132     getCellIndex : function(cell){
33133         var id = String(cell.className).match(this.cellRE);
33134         if(id){
33135             return parseInt(id[1], 10);
33136         }
33137         return 0;
33138     },
33139
33140     findHeaderIndex : function(n){
33141         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33142         return r ? this.getCellIndex(r) : false;
33143     },
33144
33145     findHeaderCell : function(n){
33146         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33147         return r ? r : false;
33148     },
33149
33150     findRowIndex : function(n){
33151         if(!n){
33152             return false;
33153         }
33154         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33155         return r ? r.rowIndex : false;
33156     },
33157
33158     findCellIndex : function(node){
33159         var stop = this.el.dom;
33160         while(node && node != stop){
33161             if(this.findRE.test(node.className)){
33162                 return this.getCellIndex(node);
33163             }
33164             node = node.parentNode;
33165         }
33166         return false;
33167     },
33168
33169     getColumnId : function(index){
33170         return this.cm.getColumnId(index);
33171     },
33172
33173     getSplitters : function()
33174     {
33175         if(this.splitterSelector){
33176            return Roo.DomQuery.select(this.splitterSelector);
33177         }else{
33178             return null;
33179       }
33180     },
33181
33182     getSplitter : function(index){
33183         return this.getSplitters()[index];
33184     },
33185
33186     onRowOver : function(e, t){
33187         var row;
33188         if((row = this.findRowIndex(t)) !== false){
33189             this.getRowComposite(row).addClass("x-grid-row-over");
33190         }
33191     },
33192
33193     onRowOut : function(e, t){
33194         var row;
33195         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33196             this.getRowComposite(row).removeClass("x-grid-row-over");
33197         }
33198     },
33199
33200     renderHeaders : function(){
33201         var cm = this.cm;
33202         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33203         var cb = [], lb = [], sb = [], lsb = [], p = {};
33204         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33205             p.cellId = "x-grid-hd-0-" + i;
33206             p.splitId = "x-grid-csplit-0-" + i;
33207             p.id = cm.getColumnId(i);
33208             p.value = cm.getColumnHeader(i) || "";
33209             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
33210             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33211             if(!cm.isLocked(i)){
33212                 cb[cb.length] = ct.apply(p);
33213                 sb[sb.length] = st.apply(p);
33214             }else{
33215                 lb[lb.length] = ct.apply(p);
33216                 lsb[lsb.length] = st.apply(p);
33217             }
33218         }
33219         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33220                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33221     },
33222
33223     updateHeaders : function(){
33224         var html = this.renderHeaders();
33225         this.lockedHd.update(html[0]);
33226         this.mainHd.update(html[1]);
33227     },
33228
33229     /**
33230      * Focuses the specified row.
33231      * @param {Number} row The row index
33232      */
33233     focusRow : function(row)
33234     {
33235         //Roo.log('GridView.focusRow');
33236         var x = this.scroller.dom.scrollLeft;
33237         this.focusCell(row, 0, false);
33238         this.scroller.dom.scrollLeft = x;
33239     },
33240
33241     /**
33242      * Focuses the specified cell.
33243      * @param {Number} row The row index
33244      * @param {Number} col The column index
33245      * @param {Boolean} hscroll false to disable horizontal scrolling
33246      */
33247     focusCell : function(row, col, hscroll)
33248     {
33249         //Roo.log('GridView.focusCell');
33250         var el = this.ensureVisible(row, col, hscroll);
33251         this.focusEl.alignTo(el, "tl-tl");
33252         if(Roo.isGecko){
33253             this.focusEl.focus();
33254         }else{
33255             this.focusEl.focus.defer(1, this.focusEl);
33256         }
33257     },
33258
33259     /**
33260      * Scrolls the specified cell into view
33261      * @param {Number} row The row index
33262      * @param {Number} col The column index
33263      * @param {Boolean} hscroll false to disable horizontal scrolling
33264      */
33265     ensureVisible : function(row, col, hscroll)
33266     {
33267         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33268         //return null; //disable for testing.
33269         if(typeof row != "number"){
33270             row = row.rowIndex;
33271         }
33272         if(row < 0 && row >= this.ds.getCount()){
33273             return  null;
33274         }
33275         col = (col !== undefined ? col : 0);
33276         var cm = this.grid.colModel;
33277         while(cm.isHidden(col)){
33278             col++;
33279         }
33280
33281         var el = this.getCell(row, col);
33282         if(!el){
33283             return null;
33284         }
33285         var c = this.scroller.dom;
33286
33287         var ctop = parseInt(el.offsetTop, 10);
33288         var cleft = parseInt(el.offsetLeft, 10);
33289         var cbot = ctop + el.offsetHeight;
33290         var cright = cleft + el.offsetWidth;
33291         
33292         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33293         var stop = parseInt(c.scrollTop, 10);
33294         var sleft = parseInt(c.scrollLeft, 10);
33295         var sbot = stop + ch;
33296         var sright = sleft + c.clientWidth;
33297         /*
33298         Roo.log('GridView.ensureVisible:' +
33299                 ' ctop:' + ctop +
33300                 ' c.clientHeight:' + c.clientHeight +
33301                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33302                 ' stop:' + stop +
33303                 ' cbot:' + cbot +
33304                 ' sbot:' + sbot +
33305                 ' ch:' + ch  
33306                 );
33307         */
33308         if(ctop < stop){
33309              c.scrollTop = ctop;
33310             //Roo.log("set scrolltop to ctop DISABLE?");
33311         }else if(cbot > sbot){
33312             //Roo.log("set scrolltop to cbot-ch");
33313             c.scrollTop = cbot-ch;
33314         }
33315         
33316         if(hscroll !== false){
33317             if(cleft < sleft){
33318                 c.scrollLeft = cleft;
33319             }else if(cright > sright){
33320                 c.scrollLeft = cright-c.clientWidth;
33321             }
33322         }
33323          
33324         return el;
33325     },
33326
33327     updateColumns : function(){
33328         this.grid.stopEditing();
33329         var cm = this.grid.colModel, colIds = this.getColumnIds();
33330         //var totalWidth = cm.getTotalWidth();
33331         var pos = 0;
33332         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33333             //if(cm.isHidden(i)) continue;
33334             var w = cm.getColumnWidth(i);
33335             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33336             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33337         }
33338         this.updateSplitters();
33339     },
33340
33341     generateRules : function(cm){
33342         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33343         Roo.util.CSS.removeStyleSheet(rulesId);
33344         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33345             var cid = cm.getColumnId(i);
33346             var align = '';
33347             if(cm.config[i].align){
33348                 align = 'text-align:'+cm.config[i].align+';';
33349             }
33350             var hidden = '';
33351             if(cm.isHidden(i)){
33352                 hidden = 'display:none;';
33353             }
33354             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33355             ruleBuf.push(
33356                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33357                     this.hdSelector, cid, " {\n", align, width, "}\n",
33358                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33359                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33360         }
33361         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33362     },
33363
33364     updateSplitters : function(){
33365         var cm = this.cm, s = this.getSplitters();
33366         if(s){ // splitters not created yet
33367             var pos = 0, locked = true;
33368             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33369                 if(cm.isHidden(i)) {
33370                     continue;
33371                 }
33372                 var w = cm.getColumnWidth(i); // make sure it's a number
33373                 if(!cm.isLocked(i) && locked){
33374                     pos = 0;
33375                     locked = false;
33376                 }
33377                 pos += w;
33378                 s[i].style.left = (pos-this.splitOffset) + "px";
33379             }
33380         }
33381     },
33382
33383     handleHiddenChange : function(colModel, colIndex, hidden){
33384         if(hidden){
33385             this.hideColumn(colIndex);
33386         }else{
33387             this.unhideColumn(colIndex);
33388         }
33389     },
33390
33391     hideColumn : function(colIndex){
33392         var cid = this.getColumnId(colIndex);
33393         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33394         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33395         if(Roo.isSafari){
33396             this.updateHeaders();
33397         }
33398         this.updateSplitters();
33399         this.layout();
33400     },
33401
33402     unhideColumn : function(colIndex){
33403         var cid = this.getColumnId(colIndex);
33404         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33405         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33406
33407         if(Roo.isSafari){
33408             this.updateHeaders();
33409         }
33410         this.updateSplitters();
33411         this.layout();
33412     },
33413
33414     insertRows : function(dm, firstRow, lastRow, isUpdate){
33415         if(firstRow == 0 && lastRow == dm.getCount()-1){
33416             this.refresh();
33417         }else{
33418             if(!isUpdate){
33419                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33420             }
33421             var s = this.getScrollState();
33422             var markup = this.renderRows(firstRow, lastRow);
33423             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33424             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33425             this.restoreScroll(s);
33426             if(!isUpdate){
33427                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33428                 this.syncRowHeights(firstRow, lastRow);
33429                 this.stripeRows(firstRow);
33430                 this.layout();
33431             }
33432         }
33433     },
33434
33435     bufferRows : function(markup, target, index){
33436         var before = null, trows = target.rows, tbody = target.tBodies[0];
33437         if(index < trows.length){
33438             before = trows[index];
33439         }
33440         var b = document.createElement("div");
33441         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33442         var rows = b.firstChild.rows;
33443         for(var i = 0, len = rows.length; i < len; i++){
33444             if(before){
33445                 tbody.insertBefore(rows[0], before);
33446             }else{
33447                 tbody.appendChild(rows[0]);
33448             }
33449         }
33450         b.innerHTML = "";
33451         b = null;
33452     },
33453
33454     deleteRows : function(dm, firstRow, lastRow){
33455         if(dm.getRowCount()<1){
33456             this.fireEvent("beforerefresh", this);
33457             this.mainBody.update("");
33458             this.lockedBody.update("");
33459             this.fireEvent("refresh", this);
33460         }else{
33461             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33462             var bt = this.getBodyTable();
33463             var tbody = bt.firstChild;
33464             var rows = bt.rows;
33465             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33466                 tbody.removeChild(rows[firstRow]);
33467             }
33468             this.stripeRows(firstRow);
33469             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33470         }
33471     },
33472
33473     updateRows : function(dataSource, firstRow, lastRow){
33474         var s = this.getScrollState();
33475         this.refresh();
33476         this.restoreScroll(s);
33477     },
33478
33479     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33480         if(!noRefresh){
33481            this.refresh();
33482         }
33483         this.updateHeaderSortState();
33484     },
33485
33486     getScrollState : function(){
33487         
33488         var sb = this.scroller.dom;
33489         return {left: sb.scrollLeft, top: sb.scrollTop};
33490     },
33491
33492     stripeRows : function(startRow){
33493         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33494             return;
33495         }
33496         startRow = startRow || 0;
33497         var rows = this.getBodyTable().rows;
33498         var lrows = this.getLockedTable().rows;
33499         var cls = ' x-grid-row-alt ';
33500         for(var i = startRow, len = rows.length; i < len; i++){
33501             var row = rows[i], lrow = lrows[i];
33502             var isAlt = ((i+1) % 2 == 0);
33503             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33504             if(isAlt == hasAlt){
33505                 continue;
33506             }
33507             if(isAlt){
33508                 row.className += " x-grid-row-alt";
33509             }else{
33510                 row.className = row.className.replace("x-grid-row-alt", "");
33511             }
33512             if(lrow){
33513                 lrow.className = row.className;
33514             }
33515         }
33516     },
33517
33518     restoreScroll : function(state){
33519         //Roo.log('GridView.restoreScroll');
33520         var sb = this.scroller.dom;
33521         sb.scrollLeft = state.left;
33522         sb.scrollTop = state.top;
33523         this.syncScroll();
33524     },
33525
33526     syncScroll : function(){
33527         //Roo.log('GridView.syncScroll');
33528         var sb = this.scroller.dom;
33529         var sh = this.mainHd.dom;
33530         var bs = this.mainBody.dom;
33531         var lv = this.lockedBody.dom;
33532         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33533         lv.scrollTop = bs.scrollTop = sb.scrollTop;
33534     },
33535
33536     handleScroll : function(e){
33537         this.syncScroll();
33538         var sb = this.scroller.dom;
33539         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33540         e.stopEvent();
33541     },
33542
33543     handleWheel : function(e){
33544         var d = e.getWheelDelta();
33545         this.scroller.dom.scrollTop -= d*22;
33546         // set this here to prevent jumpy scrolling on large tables
33547         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33548         e.stopEvent();
33549     },
33550
33551     renderRows : function(startRow, endRow){
33552         // pull in all the crap needed to render rows
33553         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33554         var colCount = cm.getColumnCount();
33555
33556         if(ds.getCount() < 1){
33557             return ["", ""];
33558         }
33559
33560         // build a map for all the columns
33561         var cs = [];
33562         for(var i = 0; i < colCount; i++){
33563             var name = cm.getDataIndex(i);
33564             cs[i] = {
33565                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33566                 renderer : cm.getRenderer(i),
33567                 id : cm.getColumnId(i),
33568                 locked : cm.isLocked(i),
33569                 has_editor : cm.isCellEditable(i)
33570             };
33571         }
33572
33573         startRow = startRow || 0;
33574         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
33575
33576         // records to render
33577         var rs = ds.getRange(startRow, endRow);
33578
33579         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
33580     },
33581
33582     // As much as I hate to duplicate code, this was branched because FireFox really hates
33583     // [].join("") on strings. The performance difference was substantial enough to
33584     // branch this function
33585     doRender : Roo.isGecko ?
33586             function(cs, rs, ds, startRow, colCount, stripe){
33587                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33588                 // buffers
33589                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33590                 
33591                 var hasListener = this.grid.hasListener('rowclass');
33592                 var rowcfg = {};
33593                 for(var j = 0, len = rs.length; j < len; j++){
33594                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
33595                     for(var i = 0; i < colCount; i++){
33596                         c = cs[i];
33597                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33598                         p.id = c.id;
33599                         p.css = p.attr = "";
33600                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33601                         if(p.value == undefined || p.value === "") {
33602                             p.value = "&#160;";
33603                         }
33604                         if(c.has_editor){
33605                             p.css += ' x-grid-editable-cell';
33606                         }
33607                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
33608                             p.css +=  ' x-grid-dirty-cell';
33609                         }
33610                         var markup = ct.apply(p);
33611                         if(!c.locked){
33612                             cb+= markup;
33613                         }else{
33614                             lcb+= markup;
33615                         }
33616                     }
33617                     var alt = [];
33618                     if(stripe && ((rowIndex+1) % 2 == 0)){
33619                         alt.push("x-grid-row-alt")
33620                     }
33621                     if(r.dirty){
33622                         alt.push(  " x-grid-dirty-row");
33623                     }
33624                     rp.cells = lcb;
33625                     if(this.getRowClass){
33626                         alt.push(this.getRowClass(r, rowIndex));
33627                     }
33628                     if (hasListener) {
33629                         rowcfg = {
33630                              
33631                             record: r,
33632                             rowIndex : rowIndex,
33633                             rowClass : ''
33634                         };
33635                         this.grid.fireEvent('rowclass', this, rowcfg);
33636                         alt.push(rowcfg.rowClass);
33637                     }
33638                     rp.alt = alt.join(" ");
33639                     lbuf+= rt.apply(rp);
33640                     rp.cells = cb;
33641                     buf+=  rt.apply(rp);
33642                 }
33643                 return [lbuf, buf];
33644             } :
33645             function(cs, rs, ds, startRow, colCount, stripe){
33646                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33647                 // buffers
33648                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33649                 var hasListener = this.grid.hasListener('rowclass');
33650  
33651                 var rowcfg = {};
33652                 for(var j = 0, len = rs.length; j < len; j++){
33653                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
33654                     for(var i = 0; i < colCount; i++){
33655                         c = cs[i];
33656                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33657                         p.id = c.id;
33658                         p.css = p.attr = "";
33659                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33660                         if(p.value == undefined || p.value === "") {
33661                             p.value = "&#160;";
33662                         }
33663                         //Roo.log(c);
33664                          if(c.has_editor){
33665                             p.css += ' x-grid-editable-cell';
33666                         }
33667                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33668                             p.css += ' x-grid-dirty-cell' 
33669                         }
33670                         
33671                         var markup = ct.apply(p);
33672                         if(!c.locked){
33673                             cb[cb.length] = markup;
33674                         }else{
33675                             lcb[lcb.length] = markup;
33676                         }
33677                     }
33678                     var alt = [];
33679                     if(stripe && ((rowIndex+1) % 2 == 0)){
33680                         alt.push( "x-grid-row-alt");
33681                     }
33682                     if(r.dirty){
33683                         alt.push(" x-grid-dirty-row");
33684                     }
33685                     rp.cells = lcb;
33686                     if(this.getRowClass){
33687                         alt.push( this.getRowClass(r, rowIndex));
33688                     }
33689                     if (hasListener) {
33690                         rowcfg = {
33691                              
33692                             record: r,
33693                             rowIndex : rowIndex,
33694                             rowClass : ''
33695                         };
33696                         this.grid.fireEvent('rowclass', this, rowcfg);
33697                         alt.push(rowcfg.rowClass);
33698                     }
33699                     
33700                     rp.alt = alt.join(" ");
33701                     rp.cells = lcb.join("");
33702                     lbuf[lbuf.length] = rt.apply(rp);
33703                     rp.cells = cb.join("");
33704                     buf[buf.length] =  rt.apply(rp);
33705                 }
33706                 return [lbuf.join(""), buf.join("")];
33707             },
33708
33709     renderBody : function(){
33710         var markup = this.renderRows();
33711         var bt = this.templates.body;
33712         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
33713     },
33714
33715     /**
33716      * Refreshes the grid
33717      * @param {Boolean} headersToo
33718      */
33719     refresh : function(headersToo){
33720         this.fireEvent("beforerefresh", this);
33721         this.grid.stopEditing();
33722         var result = this.renderBody();
33723         this.lockedBody.update(result[0]);
33724         this.mainBody.update(result[1]);
33725         if(headersToo === true){
33726             this.updateHeaders();
33727             this.updateColumns();
33728             this.updateSplitters();
33729             this.updateHeaderSortState();
33730         }
33731         this.syncRowHeights();
33732         this.layout();
33733         this.fireEvent("refresh", this);
33734     },
33735
33736     handleColumnMove : function(cm, oldIndex, newIndex){
33737         this.indexMap = null;
33738         var s = this.getScrollState();
33739         this.refresh(true);
33740         this.restoreScroll(s);
33741         this.afterMove(newIndex);
33742     },
33743
33744     afterMove : function(colIndex){
33745         if(this.enableMoveAnim && Roo.enableFx){
33746             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
33747         }
33748         // if multisort - fix sortOrder, and reload..
33749         if (this.grid.dataSource.multiSort) {
33750             // the we can call sort again..
33751             var dm = this.grid.dataSource;
33752             var cm = this.grid.colModel;
33753             var so = [];
33754             for(var i = 0; i < cm.config.length; i++ ) {
33755                 
33756                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
33757                     continue; // dont' bother, it's not in sort list or being set.
33758                 }
33759                 
33760                 so.push(cm.config[i].dataIndex);
33761             };
33762             dm.sortOrder = so;
33763             dm.load(dm.lastOptions);
33764             
33765             
33766         }
33767         
33768     },
33769
33770     updateCell : function(dm, rowIndex, dataIndex){
33771         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
33772         if(typeof colIndex == "undefined"){ // not present in grid
33773             return;
33774         }
33775         var cm = this.grid.colModel;
33776         var cell = this.getCell(rowIndex, colIndex);
33777         var cellText = this.getCellText(rowIndex, colIndex);
33778
33779         var p = {
33780             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
33781             id : cm.getColumnId(colIndex),
33782             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
33783         };
33784         var renderer = cm.getRenderer(colIndex);
33785         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
33786         if(typeof val == "undefined" || val === "") {
33787             val = "&#160;";
33788         }
33789         cellText.innerHTML = val;
33790         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
33791         this.syncRowHeights(rowIndex, rowIndex);
33792     },
33793
33794     calcColumnWidth : function(colIndex, maxRowsToMeasure){
33795         var maxWidth = 0;
33796         if(this.grid.autoSizeHeaders){
33797             var h = this.getHeaderCellMeasure(colIndex);
33798             maxWidth = Math.max(maxWidth, h.scrollWidth);
33799         }
33800         var tb, index;
33801         if(this.cm.isLocked(colIndex)){
33802             tb = this.getLockedTable();
33803             index = colIndex;
33804         }else{
33805             tb = this.getBodyTable();
33806             index = colIndex - this.cm.getLockedCount();
33807         }
33808         if(tb && tb.rows){
33809             var rows = tb.rows;
33810             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
33811             for(var i = 0; i < stopIndex; i++){
33812                 var cell = rows[i].childNodes[index].firstChild;
33813                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
33814             }
33815         }
33816         return maxWidth + /*margin for error in IE*/ 5;
33817     },
33818     /**
33819      * Autofit a column to its content.
33820      * @param {Number} colIndex
33821      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
33822      */
33823      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
33824          if(this.cm.isHidden(colIndex)){
33825              return; // can't calc a hidden column
33826          }
33827         if(forceMinSize){
33828             var cid = this.cm.getColumnId(colIndex);
33829             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
33830            if(this.grid.autoSizeHeaders){
33831                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
33832            }
33833         }
33834         var newWidth = this.calcColumnWidth(colIndex);
33835         this.cm.setColumnWidth(colIndex,
33836             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
33837         if(!suppressEvent){
33838             this.grid.fireEvent("columnresize", colIndex, newWidth);
33839         }
33840     },
33841
33842     /**
33843      * Autofits all columns to their content and then expands to fit any extra space in the grid
33844      */
33845      autoSizeColumns : function(){
33846         var cm = this.grid.colModel;
33847         var colCount = cm.getColumnCount();
33848         for(var i = 0; i < colCount; i++){
33849             this.autoSizeColumn(i, true, true);
33850         }
33851         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
33852             this.fitColumns();
33853         }else{
33854             this.updateColumns();
33855             this.layout();
33856         }
33857     },
33858
33859     /**
33860      * Autofits all columns to the grid's width proportionate with their current size
33861      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
33862      */
33863     fitColumns : function(reserveScrollSpace){
33864         var cm = this.grid.colModel;
33865         var colCount = cm.getColumnCount();
33866         var cols = [];
33867         var width = 0;
33868         var i, w;
33869         for (i = 0; i < colCount; i++){
33870             if(!cm.isHidden(i) && !cm.isFixed(i)){
33871                 w = cm.getColumnWidth(i);
33872                 cols.push(i);
33873                 cols.push(w);
33874                 width += w;
33875             }
33876         }
33877         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
33878         if(reserveScrollSpace){
33879             avail -= 17;
33880         }
33881         var frac = (avail - cm.getTotalWidth())/width;
33882         while (cols.length){
33883             w = cols.pop();
33884             i = cols.pop();
33885             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
33886         }
33887         this.updateColumns();
33888         this.layout();
33889     },
33890
33891     onRowSelect : function(rowIndex){
33892         var row = this.getRowComposite(rowIndex);
33893         row.addClass("x-grid-row-selected");
33894     },
33895
33896     onRowDeselect : function(rowIndex){
33897         var row = this.getRowComposite(rowIndex);
33898         row.removeClass("x-grid-row-selected");
33899     },
33900
33901     onCellSelect : function(row, col){
33902         var cell = this.getCell(row, col);
33903         if(cell){
33904             Roo.fly(cell).addClass("x-grid-cell-selected");
33905         }
33906     },
33907
33908     onCellDeselect : function(row, col){
33909         var cell = this.getCell(row, col);
33910         if(cell){
33911             Roo.fly(cell).removeClass("x-grid-cell-selected");
33912         }
33913     },
33914
33915     updateHeaderSortState : function(){
33916         
33917         // sort state can be single { field: xxx, direction : yyy}
33918         // or   { xxx=>ASC , yyy : DESC ..... }
33919         
33920         var mstate = {};
33921         if (!this.ds.multiSort) { 
33922             var state = this.ds.getSortState();
33923             if(!state){
33924                 return;
33925             }
33926             mstate[state.field] = state.direction;
33927             // FIXME... - this is not used here.. but might be elsewhere..
33928             this.sortState = state;
33929             
33930         } else {
33931             mstate = this.ds.sortToggle;
33932         }
33933         //remove existing sort classes..
33934         
33935         var sc = this.sortClasses;
33936         var hds = this.el.select(this.headerSelector).removeClass(sc);
33937         
33938         for(var f in mstate) {
33939         
33940             var sortColumn = this.cm.findColumnIndex(f);
33941             
33942             if(sortColumn != -1){
33943                 var sortDir = mstate[f];        
33944                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
33945             }
33946         }
33947         
33948          
33949         
33950     },
33951
33952
33953     handleHeaderClick : function(g, index,e){
33954         
33955         Roo.log("header click");
33956         
33957         if (Roo.isTouch) {
33958             // touch events on header are handled by context
33959             this.handleHdCtx(g,index,e);
33960             return;
33961         }
33962         
33963         
33964         if(this.headersDisabled){
33965             return;
33966         }
33967         var dm = g.dataSource, cm = g.colModel;
33968         if(!cm.isSortable(index)){
33969             return;
33970         }
33971         g.stopEditing();
33972         
33973         if (dm.multiSort) {
33974             // update the sortOrder
33975             var so = [];
33976             for(var i = 0; i < cm.config.length; i++ ) {
33977                 
33978                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
33979                     continue; // dont' bother, it's not in sort list or being set.
33980                 }
33981                 
33982                 so.push(cm.config[i].dataIndex);
33983             };
33984             dm.sortOrder = so;
33985         }
33986         
33987         
33988         dm.sort(cm.getDataIndex(index));
33989     },
33990
33991
33992     destroy : function(){
33993         if(this.colMenu){
33994             this.colMenu.removeAll();
33995             Roo.menu.MenuMgr.unregister(this.colMenu);
33996             this.colMenu.getEl().remove();
33997             delete this.colMenu;
33998         }
33999         if(this.hmenu){
34000             this.hmenu.removeAll();
34001             Roo.menu.MenuMgr.unregister(this.hmenu);
34002             this.hmenu.getEl().remove();
34003             delete this.hmenu;
34004         }
34005         if(this.grid.enableColumnMove){
34006             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34007             if(dds){
34008                 for(var dd in dds){
34009                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34010                         var elid = dds[dd].dragElId;
34011                         dds[dd].unreg();
34012                         Roo.get(elid).remove();
34013                     } else if(dds[dd].config.isTarget){
34014                         dds[dd].proxyTop.remove();
34015                         dds[dd].proxyBottom.remove();
34016                         dds[dd].unreg();
34017                     }
34018                     if(Roo.dd.DDM.locationCache[dd]){
34019                         delete Roo.dd.DDM.locationCache[dd];
34020                     }
34021                 }
34022                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34023             }
34024         }
34025         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34026         this.bind(null, null);
34027         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34028     },
34029
34030     handleLockChange : function(){
34031         this.refresh(true);
34032     },
34033
34034     onDenyColumnLock : function(){
34035
34036     },
34037
34038     onDenyColumnHide : function(){
34039
34040     },
34041
34042     handleHdMenuClick : function(item){
34043         var index = this.hdCtxIndex;
34044         var cm = this.cm, ds = this.ds;
34045         switch(item.id){
34046             case "asc":
34047                 ds.sort(cm.getDataIndex(index), "ASC");
34048                 break;
34049             case "desc":
34050                 ds.sort(cm.getDataIndex(index), "DESC");
34051                 break;
34052             case "lock":
34053                 var lc = cm.getLockedCount();
34054                 if(cm.getColumnCount(true) <= lc+1){
34055                     this.onDenyColumnLock();
34056                     return;
34057                 }
34058                 if(lc != index){
34059                     cm.setLocked(index, true, true);
34060                     cm.moveColumn(index, lc);
34061                     this.grid.fireEvent("columnmove", index, lc);
34062                 }else{
34063                     cm.setLocked(index, true);
34064                 }
34065             break;
34066             case "unlock":
34067                 var lc = cm.getLockedCount();
34068                 if((lc-1) != index){
34069                     cm.setLocked(index, false, true);
34070                     cm.moveColumn(index, lc-1);
34071                     this.grid.fireEvent("columnmove", index, lc-1);
34072                 }else{
34073                     cm.setLocked(index, false);
34074                 }
34075             break;
34076             case 'wider': // used to expand cols on touch..
34077             case 'narrow':
34078                 var cw = cm.getColumnWidth(index);
34079                 cw += (item.id == 'wider' ? 1 : -1) * 50;
34080                 cw = Math.max(0, cw);
34081                 cw = Math.min(cw,4000);
34082                 cm.setColumnWidth(index, cw);
34083                 break;
34084                 
34085             default:
34086                 index = cm.getIndexById(item.id.substr(4));
34087                 if(index != -1){
34088                     if(item.checked && cm.getColumnCount(true) <= 1){
34089                         this.onDenyColumnHide();
34090                         return false;
34091                     }
34092                     cm.setHidden(index, item.checked);
34093                 }
34094         }
34095         return true;
34096     },
34097
34098     beforeColMenuShow : function(){
34099         var cm = this.cm,  colCount = cm.getColumnCount();
34100         this.colMenu.removeAll();
34101         for(var i = 0; i < colCount; i++){
34102             this.colMenu.add(new Roo.menu.CheckItem({
34103                 id: "col-"+cm.getColumnId(i),
34104                 text: cm.getColumnHeader(i),
34105                 checked: !cm.isHidden(i),
34106                 hideOnClick:false
34107             }));
34108         }
34109     },
34110
34111     handleHdCtx : function(g, index, e){
34112         e.stopEvent();
34113         var hd = this.getHeaderCell(index);
34114         this.hdCtxIndex = index;
34115         var ms = this.hmenu.items, cm = this.cm;
34116         ms.get("asc").setDisabled(!cm.isSortable(index));
34117         ms.get("desc").setDisabled(!cm.isSortable(index));
34118         if(this.grid.enableColLock !== false){
34119             ms.get("lock").setDisabled(cm.isLocked(index));
34120             ms.get("unlock").setDisabled(!cm.isLocked(index));
34121         }
34122         this.hmenu.show(hd, "tl-bl");
34123     },
34124
34125     handleHdOver : function(e){
34126         var hd = this.findHeaderCell(e.getTarget());
34127         if(hd && !this.headersDisabled){
34128             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34129                this.fly(hd).addClass("x-grid-hd-over");
34130             }
34131         }
34132     },
34133
34134     handleHdOut : function(e){
34135         var hd = this.findHeaderCell(e.getTarget());
34136         if(hd){
34137             this.fly(hd).removeClass("x-grid-hd-over");
34138         }
34139     },
34140
34141     handleSplitDblClick : function(e, t){
34142         var i = this.getCellIndex(t);
34143         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34144             this.autoSizeColumn(i, true);
34145             this.layout();
34146         }
34147     },
34148
34149     render : function(){
34150
34151         var cm = this.cm;
34152         var colCount = cm.getColumnCount();
34153
34154         if(this.grid.monitorWindowResize === true){
34155             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34156         }
34157         var header = this.renderHeaders();
34158         var body = this.templates.body.apply({rows:""});
34159         var html = this.templates.master.apply({
34160             lockedBody: body,
34161             body: body,
34162             lockedHeader: header[0],
34163             header: header[1]
34164         });
34165
34166         //this.updateColumns();
34167
34168         this.grid.getGridEl().dom.innerHTML = html;
34169
34170         this.initElements();
34171         
34172         // a kludge to fix the random scolling effect in webkit
34173         this.el.on("scroll", function() {
34174             this.el.dom.scrollTop=0; // hopefully not recursive..
34175         },this);
34176
34177         this.scroller.on("scroll", this.handleScroll, this);
34178         this.lockedBody.on("mousewheel", this.handleWheel, this);
34179         this.mainBody.on("mousewheel", this.handleWheel, this);
34180
34181         this.mainHd.on("mouseover", this.handleHdOver, this);
34182         this.mainHd.on("mouseout", this.handleHdOut, this);
34183         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34184                 {delegate: "."+this.splitClass});
34185
34186         this.lockedHd.on("mouseover", this.handleHdOver, this);
34187         this.lockedHd.on("mouseout", this.handleHdOut, this);
34188         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34189                 {delegate: "."+this.splitClass});
34190
34191         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34192             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34193         }
34194
34195         this.updateSplitters();
34196
34197         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34198             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34199             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34200         }
34201
34202         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34203             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34204             this.hmenu.add(
34205                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34206                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34207             );
34208             if(this.grid.enableColLock !== false){
34209                 this.hmenu.add('-',
34210                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34211                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34212                 );
34213             }
34214             if (Roo.isTouch) {
34215                  this.hmenu.add('-',
34216                     {id:"wider", text: this.columnsWiderText},
34217                     {id:"narrow", text: this.columnsNarrowText }
34218                 );
34219                 
34220                  
34221             }
34222             
34223             if(this.grid.enableColumnHide !== false){
34224
34225                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34226                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34227                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34228
34229                 this.hmenu.add('-',
34230                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34231                 );
34232             }
34233             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34234
34235             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34236         }
34237
34238         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34239             this.dd = new Roo.grid.GridDragZone(this.grid, {
34240                 ddGroup : this.grid.ddGroup || 'GridDD'
34241             });
34242             
34243         }
34244
34245         /*
34246         for(var i = 0; i < colCount; i++){
34247             if(cm.isHidden(i)){
34248                 this.hideColumn(i);
34249             }
34250             if(cm.config[i].align){
34251                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34252                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34253             }
34254         }*/
34255         
34256         this.updateHeaderSortState();
34257
34258         this.beforeInitialResize();
34259         this.layout(true);
34260
34261         // two part rendering gives faster view to the user
34262         this.renderPhase2.defer(1, this);
34263     },
34264
34265     renderPhase2 : function(){
34266         // render the rows now
34267         this.refresh();
34268         if(this.grid.autoSizeColumns){
34269             this.autoSizeColumns();
34270         }
34271     },
34272
34273     beforeInitialResize : function(){
34274
34275     },
34276
34277     onColumnSplitterMoved : function(i, w){
34278         this.userResized = true;
34279         var cm = this.grid.colModel;
34280         cm.setColumnWidth(i, w, true);
34281         var cid = cm.getColumnId(i);
34282         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34283         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34284         this.updateSplitters();
34285         this.layout();
34286         this.grid.fireEvent("columnresize", i, w);
34287     },
34288
34289     syncRowHeights : function(startIndex, endIndex){
34290         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34291             startIndex = startIndex || 0;
34292             var mrows = this.getBodyTable().rows;
34293             var lrows = this.getLockedTable().rows;
34294             var len = mrows.length-1;
34295             endIndex = Math.min(endIndex || len, len);
34296             for(var i = startIndex; i <= endIndex; i++){
34297                 var m = mrows[i], l = lrows[i];
34298                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34299                 m.style.height = l.style.height = h + "px";
34300             }
34301         }
34302     },
34303
34304     layout : function(initialRender, is2ndPass){
34305         var g = this.grid;
34306         var auto = g.autoHeight;
34307         var scrollOffset = 16;
34308         var c = g.getGridEl(), cm = this.cm,
34309                 expandCol = g.autoExpandColumn,
34310                 gv = this;
34311         //c.beginMeasure();
34312
34313         if(!c.dom.offsetWidth){ // display:none?
34314             if(initialRender){
34315                 this.lockedWrap.show();
34316                 this.mainWrap.show();
34317             }
34318             return;
34319         }
34320
34321         var hasLock = this.cm.isLocked(0);
34322
34323         var tbh = this.headerPanel.getHeight();
34324         var bbh = this.footerPanel.getHeight();
34325
34326         if(auto){
34327             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34328             var newHeight = ch + c.getBorderWidth("tb");
34329             if(g.maxHeight){
34330                 newHeight = Math.min(g.maxHeight, newHeight);
34331             }
34332             c.setHeight(newHeight);
34333         }
34334
34335         if(g.autoWidth){
34336             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34337         }
34338
34339         var s = this.scroller;
34340
34341         var csize = c.getSize(true);
34342
34343         this.el.setSize(csize.width, csize.height);
34344
34345         this.headerPanel.setWidth(csize.width);
34346         this.footerPanel.setWidth(csize.width);
34347
34348         var hdHeight = this.mainHd.getHeight();
34349         var vw = csize.width;
34350         var vh = csize.height - (tbh + bbh);
34351
34352         s.setSize(vw, vh);
34353
34354         var bt = this.getBodyTable();
34355         
34356         if(cm.getLockedCount() == cm.config.length){
34357             bt = this.getLockedTable();
34358         }
34359         
34360         var ltWidth = hasLock ?
34361                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34362
34363         var scrollHeight = bt.offsetHeight;
34364         var scrollWidth = ltWidth + bt.offsetWidth;
34365         var vscroll = false, hscroll = false;
34366
34367         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34368
34369         var lw = this.lockedWrap, mw = this.mainWrap;
34370         var lb = this.lockedBody, mb = this.mainBody;
34371
34372         setTimeout(function(){
34373             var t = s.dom.offsetTop;
34374             var w = s.dom.clientWidth,
34375                 h = s.dom.clientHeight;
34376
34377             lw.setTop(t);
34378             lw.setSize(ltWidth, h);
34379
34380             mw.setLeftTop(ltWidth, t);
34381             mw.setSize(w-ltWidth, h);
34382
34383             lb.setHeight(h-hdHeight);
34384             mb.setHeight(h-hdHeight);
34385
34386             if(is2ndPass !== true && !gv.userResized && expandCol){
34387                 // high speed resize without full column calculation
34388                 
34389                 var ci = cm.getIndexById(expandCol);
34390                 if (ci < 0) {
34391                     ci = cm.findColumnIndex(expandCol);
34392                 }
34393                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34394                 var expandId = cm.getColumnId(ci);
34395                 var  tw = cm.getTotalWidth(false);
34396                 var currentWidth = cm.getColumnWidth(ci);
34397                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34398                 if(currentWidth != cw){
34399                     cm.setColumnWidth(ci, cw, true);
34400                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34401                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34402                     gv.updateSplitters();
34403                     gv.layout(false, true);
34404                 }
34405             }
34406
34407             if(initialRender){
34408                 lw.show();
34409                 mw.show();
34410             }
34411             //c.endMeasure();
34412         }, 10);
34413     },
34414
34415     onWindowResize : function(){
34416         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34417             return;
34418         }
34419         this.layout();
34420     },
34421
34422     appendFooter : function(parentEl){
34423         return null;
34424     },
34425
34426     sortAscText : "Sort Ascending",
34427     sortDescText : "Sort Descending",
34428     lockText : "Lock Column",
34429     unlockText : "Unlock Column",
34430     columnsText : "Columns",
34431  
34432     columnsWiderText : "Wider",
34433     columnsNarrowText : "Thinner"
34434 });
34435
34436
34437 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34438     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34439     this.proxy.el.addClass('x-grid3-col-dd');
34440 };
34441
34442 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34443     handleMouseDown : function(e){
34444
34445     },
34446
34447     callHandleMouseDown : function(e){
34448         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34449     }
34450 });
34451 /*
34452  * Based on:
34453  * Ext JS Library 1.1.1
34454  * Copyright(c) 2006-2007, Ext JS, LLC.
34455  *
34456  * Originally Released Under LGPL - original licence link has changed is not relivant.
34457  *
34458  * Fork - LGPL
34459  * <script type="text/javascript">
34460  */
34461  
34462 // private
34463 // This is a support class used internally by the Grid components
34464 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34465     this.grid = grid;
34466     this.view = grid.getView();
34467     this.proxy = this.view.resizeProxy;
34468     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34469         "gridSplitters" + this.grid.getGridEl().id, {
34470         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34471     });
34472     this.setHandleElId(Roo.id(hd));
34473     this.setOuterHandleElId(Roo.id(hd2));
34474     this.scroll = false;
34475 };
34476 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34477     fly: Roo.Element.fly,
34478
34479     b4StartDrag : function(x, y){
34480         this.view.headersDisabled = true;
34481         this.proxy.setHeight(this.view.mainWrap.getHeight());
34482         var w = this.cm.getColumnWidth(this.cellIndex);
34483         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34484         this.resetConstraints();
34485         this.setXConstraint(minw, 1000);
34486         this.setYConstraint(0, 0);
34487         this.minX = x - minw;
34488         this.maxX = x + 1000;
34489         this.startPos = x;
34490         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34491     },
34492
34493
34494     handleMouseDown : function(e){
34495         ev = Roo.EventObject.setEvent(e);
34496         var t = this.fly(ev.getTarget());
34497         if(t.hasClass("x-grid-split")){
34498             this.cellIndex = this.view.getCellIndex(t.dom);
34499             this.split = t.dom;
34500             this.cm = this.grid.colModel;
34501             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34502                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34503             }
34504         }
34505     },
34506
34507     endDrag : function(e){
34508         this.view.headersDisabled = false;
34509         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34510         var diff = endX - this.startPos;
34511         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34512     },
34513
34514     autoOffset : function(){
34515         this.setDelta(0,0);
34516     }
34517 });/*
34518  * Based on:
34519  * Ext JS Library 1.1.1
34520  * Copyright(c) 2006-2007, Ext JS, LLC.
34521  *
34522  * Originally Released Under LGPL - original licence link has changed is not relivant.
34523  *
34524  * Fork - LGPL
34525  * <script type="text/javascript">
34526  */
34527  
34528 // private
34529 // This is a support class used internally by the Grid components
34530 Roo.grid.GridDragZone = function(grid, config){
34531     this.view = grid.getView();
34532     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34533     if(this.view.lockedBody){
34534         this.setHandleElId(Roo.id(this.view.mainBody.dom));
34535         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
34536     }
34537     this.scroll = false;
34538     this.grid = grid;
34539     this.ddel = document.createElement('div');
34540     this.ddel.className = 'x-grid-dd-wrap';
34541 };
34542
34543 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
34544     ddGroup : "GridDD",
34545
34546     getDragData : function(e){
34547         var t = Roo.lib.Event.getTarget(e);
34548         var rowIndex = this.view.findRowIndex(t);
34549         var sm = this.grid.selModel;
34550             
34551         //Roo.log(rowIndex);
34552         
34553         if (sm.getSelectedCell) {
34554             // cell selection..
34555             if (!sm.getSelectedCell()) {
34556                 return false;
34557             }
34558             if (rowIndex != sm.getSelectedCell()[0]) {
34559                 return false;
34560             }
34561         
34562         }
34563         
34564         if(rowIndex !== false){
34565             
34566             // if editorgrid.. 
34567             
34568             
34569             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
34570                
34571             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
34572               //  
34573             //}
34574             if (e.hasModifier()){
34575                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
34576             }
34577             
34578             Roo.log("getDragData");
34579             
34580             return {
34581                 grid: this.grid,
34582                 ddel: this.ddel,
34583                 rowIndex: rowIndex,
34584                 selections:sm.getSelections ? sm.getSelections() : (
34585                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
34586                 )
34587             };
34588         }
34589         return false;
34590     },
34591
34592     onInitDrag : function(e){
34593         var data = this.dragData;
34594         this.ddel.innerHTML = this.grid.getDragDropText();
34595         this.proxy.update(this.ddel);
34596         // fire start drag?
34597     },
34598
34599     afterRepair : function(){
34600         this.dragging = false;
34601     },
34602
34603     getRepairXY : function(e, data){
34604         return false;
34605     },
34606
34607     onEndDrag : function(data, e){
34608         // fire end drag?
34609     },
34610
34611     onValidDrop : function(dd, e, id){
34612         // fire drag drop?
34613         this.hideProxy();
34614     },
34615
34616     beforeInvalidDrop : function(e, id){
34617
34618     }
34619 });/*
34620  * Based on:
34621  * Ext JS Library 1.1.1
34622  * Copyright(c) 2006-2007, Ext JS, LLC.
34623  *
34624  * Originally Released Under LGPL - original licence link has changed is not relivant.
34625  *
34626  * Fork - LGPL
34627  * <script type="text/javascript">
34628  */
34629  
34630
34631 /**
34632  * @class Roo.grid.ColumnModel
34633  * @extends Roo.util.Observable
34634  * This is the default implementation of a ColumnModel used by the Grid. It defines
34635  * the columns in the grid.
34636  * <br>Usage:<br>
34637  <pre><code>
34638  var colModel = new Roo.grid.ColumnModel([
34639         {header: "Ticker", width: 60, sortable: true, locked: true},
34640         {header: "Company Name", width: 150, sortable: true},
34641         {header: "Market Cap.", width: 100, sortable: true},
34642         {header: "$ Sales", width: 100, sortable: true, renderer: money},
34643         {header: "Employees", width: 100, sortable: true, resizable: false}
34644  ]);
34645  </code></pre>
34646  * <p>
34647  
34648  * The config options listed for this class are options which may appear in each
34649  * individual column definition.
34650  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
34651  * @constructor
34652  * @param {Object} config An Array of column config objects. See this class's
34653  * config objects for details.
34654 */
34655 Roo.grid.ColumnModel = function(config){
34656         /**
34657      * The config passed into the constructor
34658      */
34659     this.config = config;
34660     this.lookup = {};
34661
34662     // if no id, create one
34663     // if the column does not have a dataIndex mapping,
34664     // map it to the order it is in the config
34665     for(var i = 0, len = config.length; i < len; i++){
34666         var c = config[i];
34667         if(typeof c.dataIndex == "undefined"){
34668             c.dataIndex = i;
34669         }
34670         if(typeof c.renderer == "string"){
34671             c.renderer = Roo.util.Format[c.renderer];
34672         }
34673         if(typeof c.id == "undefined"){
34674             c.id = Roo.id();
34675         }
34676         if(c.editor && c.editor.xtype){
34677             c.editor  = Roo.factory(c.editor, Roo.grid);
34678         }
34679         if(c.editor && c.editor.isFormField){
34680             c.editor = new Roo.grid.GridEditor(c.editor);
34681         }
34682         this.lookup[c.id] = c;
34683     }
34684
34685     /**
34686      * The width of columns which have no width specified (defaults to 100)
34687      * @type Number
34688      */
34689     this.defaultWidth = 100;
34690
34691     /**
34692      * Default sortable of columns which have no sortable specified (defaults to false)
34693      * @type Boolean
34694      */
34695     this.defaultSortable = false;
34696
34697     this.addEvents({
34698         /**
34699              * @event widthchange
34700              * Fires when the width of a column changes.
34701              * @param {ColumnModel} this
34702              * @param {Number} columnIndex The column index
34703              * @param {Number} newWidth The new width
34704              */
34705             "widthchange": true,
34706         /**
34707              * @event headerchange
34708              * Fires when the text of a header changes.
34709              * @param {ColumnModel} this
34710              * @param {Number} columnIndex The column index
34711              * @param {Number} newText The new header text
34712              */
34713             "headerchange": true,
34714         /**
34715              * @event hiddenchange
34716              * Fires when a column is hidden or "unhidden".
34717              * @param {ColumnModel} this
34718              * @param {Number} columnIndex The column index
34719              * @param {Boolean} hidden true if hidden, false otherwise
34720              */
34721             "hiddenchange": true,
34722             /**
34723          * @event columnmoved
34724          * Fires when a column is moved.
34725          * @param {ColumnModel} this
34726          * @param {Number} oldIndex
34727          * @param {Number} newIndex
34728          */
34729         "columnmoved" : true,
34730         /**
34731          * @event columlockchange
34732          * Fires when a column's locked state is changed
34733          * @param {ColumnModel} this
34734          * @param {Number} colIndex
34735          * @param {Boolean} locked true if locked
34736          */
34737         "columnlockchange" : true
34738     });
34739     Roo.grid.ColumnModel.superclass.constructor.call(this);
34740 };
34741 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
34742     /**
34743      * @cfg {String} header The header text to display in the Grid view.
34744      */
34745     /**
34746      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
34747      * {@link Roo.data.Record} definition from which to draw the column's value. If not
34748      * specified, the column's index is used as an index into the Record's data Array.
34749      */
34750     /**
34751      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
34752      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
34753      */
34754     /**
34755      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
34756      * Defaults to the value of the {@link #defaultSortable} property.
34757      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
34758      */
34759     /**
34760      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
34761      */
34762     /**
34763      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
34764      */
34765     /**
34766      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
34767      */
34768     /**
34769      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
34770      */
34771     /**
34772      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
34773      * given the cell's data value. See {@link #setRenderer}. If not specified, the
34774      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
34775      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
34776      */
34777        /**
34778      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
34779      */
34780     /**
34781      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
34782      */
34783     /**
34784      * @cfg {String} cursor (Optional)
34785      */
34786     /**
34787      * @cfg {String} tooltip (Optional)
34788      */
34789     /**
34790      * @cfg {Number} xs (Optional)
34791      */
34792     /**
34793      * @cfg {Number} sm (Optional)
34794      */
34795     /**
34796      * @cfg {Number} md (Optional)
34797      */
34798     /**
34799      * @cfg {Number} lg (Optional)
34800      */
34801     /**
34802      * Returns the id of the column at the specified index.
34803      * @param {Number} index The column index
34804      * @return {String} the id
34805      */
34806     getColumnId : function(index){
34807         return this.config[index].id;
34808     },
34809
34810     /**
34811      * Returns the column for a specified id.
34812      * @param {String} id The column id
34813      * @return {Object} the column
34814      */
34815     getColumnById : function(id){
34816         return this.lookup[id];
34817     },
34818
34819     
34820     /**
34821      * Returns the column for a specified dataIndex.
34822      * @param {String} dataIndex The column dataIndex
34823      * @return {Object|Boolean} the column or false if not found
34824      */
34825     getColumnByDataIndex: function(dataIndex){
34826         var index = this.findColumnIndex(dataIndex);
34827         return index > -1 ? this.config[index] : false;
34828     },
34829     
34830     /**
34831      * Returns the index for a specified column id.
34832      * @param {String} id The column id
34833      * @return {Number} the index, or -1 if not found
34834      */
34835     getIndexById : function(id){
34836         for(var i = 0, len = this.config.length; i < len; i++){
34837             if(this.config[i].id == id){
34838                 return i;
34839             }
34840         }
34841         return -1;
34842     },
34843     
34844     /**
34845      * Returns the index for a specified column dataIndex.
34846      * @param {String} dataIndex The column dataIndex
34847      * @return {Number} the index, or -1 if not found
34848      */
34849     
34850     findColumnIndex : function(dataIndex){
34851         for(var i = 0, len = this.config.length; i < len; i++){
34852             if(this.config[i].dataIndex == dataIndex){
34853                 return i;
34854             }
34855         }
34856         return -1;
34857     },
34858     
34859     
34860     moveColumn : function(oldIndex, newIndex){
34861         var c = this.config[oldIndex];
34862         this.config.splice(oldIndex, 1);
34863         this.config.splice(newIndex, 0, c);
34864         this.dataMap = null;
34865         this.fireEvent("columnmoved", this, oldIndex, newIndex);
34866     },
34867
34868     isLocked : function(colIndex){
34869         return this.config[colIndex].locked === true;
34870     },
34871
34872     setLocked : function(colIndex, value, suppressEvent){
34873         if(this.isLocked(colIndex) == value){
34874             return;
34875         }
34876         this.config[colIndex].locked = value;
34877         if(!suppressEvent){
34878             this.fireEvent("columnlockchange", this, colIndex, value);
34879         }
34880     },
34881
34882     getTotalLockedWidth : function(){
34883         var totalWidth = 0;
34884         for(var i = 0; i < this.config.length; i++){
34885             if(this.isLocked(i) && !this.isHidden(i)){
34886                 this.totalWidth += this.getColumnWidth(i);
34887             }
34888         }
34889         return totalWidth;
34890     },
34891
34892     getLockedCount : function(){
34893         for(var i = 0, len = this.config.length; i < len; i++){
34894             if(!this.isLocked(i)){
34895                 return i;
34896             }
34897         }
34898         
34899         return this.config.length;
34900     },
34901
34902     /**
34903      * Returns the number of columns.
34904      * @return {Number}
34905      */
34906     getColumnCount : function(visibleOnly){
34907         if(visibleOnly === true){
34908             var c = 0;
34909             for(var i = 0, len = this.config.length; i < len; i++){
34910                 if(!this.isHidden(i)){
34911                     c++;
34912                 }
34913             }
34914             return c;
34915         }
34916         return this.config.length;
34917     },
34918
34919     /**
34920      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
34921      * @param {Function} fn
34922      * @param {Object} scope (optional)
34923      * @return {Array} result
34924      */
34925     getColumnsBy : function(fn, scope){
34926         var r = [];
34927         for(var i = 0, len = this.config.length; i < len; i++){
34928             var c = this.config[i];
34929             if(fn.call(scope||this, c, i) === true){
34930                 r[r.length] = c;
34931             }
34932         }
34933         return r;
34934     },
34935
34936     /**
34937      * Returns true if the specified column is sortable.
34938      * @param {Number} col The column index
34939      * @return {Boolean}
34940      */
34941     isSortable : function(col){
34942         if(typeof this.config[col].sortable == "undefined"){
34943             return this.defaultSortable;
34944         }
34945         return this.config[col].sortable;
34946     },
34947
34948     /**
34949      * Returns the rendering (formatting) function defined for the column.
34950      * @param {Number} col The column index.
34951      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
34952      */
34953     getRenderer : function(col){
34954         if(!this.config[col].renderer){
34955             return Roo.grid.ColumnModel.defaultRenderer;
34956         }
34957         return this.config[col].renderer;
34958     },
34959
34960     /**
34961      * Sets the rendering (formatting) function for a column.
34962      * @param {Number} col The column index
34963      * @param {Function} fn The function to use to process the cell's raw data
34964      * to return HTML markup for the grid view. The render function is called with
34965      * the following parameters:<ul>
34966      * <li>Data value.</li>
34967      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
34968      * <li>css A CSS style string to apply to the table cell.</li>
34969      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
34970      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
34971      * <li>Row index</li>
34972      * <li>Column index</li>
34973      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
34974      */
34975     setRenderer : function(col, fn){
34976         this.config[col].renderer = fn;
34977     },
34978
34979     /**
34980      * Returns the width for the specified column.
34981      * @param {Number} col The column index
34982      * @return {Number}
34983      */
34984     getColumnWidth : function(col){
34985         return this.config[col].width * 1 || this.defaultWidth;
34986     },
34987
34988     /**
34989      * Sets the width for a column.
34990      * @param {Number} col The column index
34991      * @param {Number} width The new width
34992      */
34993     setColumnWidth : function(col, width, suppressEvent){
34994         this.config[col].width = width;
34995         this.totalWidth = null;
34996         if(!suppressEvent){
34997              this.fireEvent("widthchange", this, col, width);
34998         }
34999     },
35000
35001     /**
35002      * Returns the total width of all columns.
35003      * @param {Boolean} includeHidden True to include hidden column widths
35004      * @return {Number}
35005      */
35006     getTotalWidth : function(includeHidden){
35007         if(!this.totalWidth){
35008             this.totalWidth = 0;
35009             for(var i = 0, len = this.config.length; i < len; i++){
35010                 if(includeHidden || !this.isHidden(i)){
35011                     this.totalWidth += this.getColumnWidth(i);
35012                 }
35013             }
35014         }
35015         return this.totalWidth;
35016     },
35017
35018     /**
35019      * Returns the header for the specified column.
35020      * @param {Number} col The column index
35021      * @return {String}
35022      */
35023     getColumnHeader : function(col){
35024         return this.config[col].header;
35025     },
35026
35027     /**
35028      * Sets the header for a column.
35029      * @param {Number} col The column index
35030      * @param {String} header The new header
35031      */
35032     setColumnHeader : function(col, header){
35033         this.config[col].header = header;
35034         this.fireEvent("headerchange", this, col, header);
35035     },
35036
35037     /**
35038      * Returns the tooltip for the specified column.
35039      * @param {Number} col The column index
35040      * @return {String}
35041      */
35042     getColumnTooltip : function(col){
35043             return this.config[col].tooltip;
35044     },
35045     /**
35046      * Sets the tooltip for a column.
35047      * @param {Number} col The column index
35048      * @param {String} tooltip The new tooltip
35049      */
35050     setColumnTooltip : function(col, tooltip){
35051             this.config[col].tooltip = tooltip;
35052     },
35053
35054     /**
35055      * Returns the dataIndex for the specified column.
35056      * @param {Number} col The column index
35057      * @return {Number}
35058      */
35059     getDataIndex : function(col){
35060         return this.config[col].dataIndex;
35061     },
35062
35063     /**
35064      * Sets the dataIndex for a column.
35065      * @param {Number} col The column index
35066      * @param {Number} dataIndex The new dataIndex
35067      */
35068     setDataIndex : function(col, dataIndex){
35069         this.config[col].dataIndex = dataIndex;
35070     },
35071
35072     
35073     
35074     /**
35075      * Returns true if the cell is editable.
35076      * @param {Number} colIndex The column index
35077      * @param {Number} rowIndex The row index - this is nto actually used..?
35078      * @return {Boolean}
35079      */
35080     isCellEditable : function(colIndex, rowIndex){
35081         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35082     },
35083
35084     /**
35085      * Returns the editor defined for the cell/column.
35086      * return false or null to disable editing.
35087      * @param {Number} colIndex The column index
35088      * @param {Number} rowIndex The row index
35089      * @return {Object}
35090      */
35091     getCellEditor : function(colIndex, rowIndex){
35092         return this.config[colIndex].editor;
35093     },
35094
35095     /**
35096      * Sets if a column is editable.
35097      * @param {Number} col The column index
35098      * @param {Boolean} editable True if the column is editable
35099      */
35100     setEditable : function(col, editable){
35101         this.config[col].editable = editable;
35102     },
35103
35104
35105     /**
35106      * Returns true if the column is hidden.
35107      * @param {Number} colIndex The column index
35108      * @return {Boolean}
35109      */
35110     isHidden : function(colIndex){
35111         return this.config[colIndex].hidden;
35112     },
35113
35114
35115     /**
35116      * Returns true if the column width cannot be changed
35117      */
35118     isFixed : function(colIndex){
35119         return this.config[colIndex].fixed;
35120     },
35121
35122     /**
35123      * Returns true if the column can be resized
35124      * @return {Boolean}
35125      */
35126     isResizable : function(colIndex){
35127         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35128     },
35129     /**
35130      * Sets if a column is hidden.
35131      * @param {Number} colIndex The column index
35132      * @param {Boolean} hidden True if the column is hidden
35133      */
35134     setHidden : function(colIndex, hidden){
35135         this.config[colIndex].hidden = hidden;
35136         this.totalWidth = null;
35137         this.fireEvent("hiddenchange", this, colIndex, hidden);
35138     },
35139
35140     /**
35141      * Sets the editor for a column.
35142      * @param {Number} col The column index
35143      * @param {Object} editor The editor object
35144      */
35145     setEditor : function(col, editor){
35146         this.config[col].editor = editor;
35147     }
35148 });
35149
35150 Roo.grid.ColumnModel.defaultRenderer = function(value)
35151 {
35152     if(typeof value == "object") {
35153         return value;
35154     }
35155         if(typeof value == "string" && value.length < 1){
35156             return "&#160;";
35157         }
35158     
35159         return String.format("{0}", value);
35160 };
35161
35162 // Alias for backwards compatibility
35163 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35164 /*
35165  * Based on:
35166  * Ext JS Library 1.1.1
35167  * Copyright(c) 2006-2007, Ext JS, LLC.
35168  *
35169  * Originally Released Under LGPL - original licence link has changed is not relivant.
35170  *
35171  * Fork - LGPL
35172  * <script type="text/javascript">
35173  */
35174
35175 /**
35176  * @class Roo.grid.AbstractSelectionModel
35177  * @extends Roo.util.Observable
35178  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35179  * implemented by descendant classes.  This class should not be directly instantiated.
35180  * @constructor
35181  */
35182 Roo.grid.AbstractSelectionModel = function(){
35183     this.locked = false;
35184     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35185 };
35186
35187 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35188     /** @ignore Called by the grid automatically. Do not call directly. */
35189     init : function(grid){
35190         this.grid = grid;
35191         this.initEvents();
35192     },
35193
35194     /**
35195      * Locks the selections.
35196      */
35197     lock : function(){
35198         this.locked = true;
35199     },
35200
35201     /**
35202      * Unlocks the selections.
35203      */
35204     unlock : function(){
35205         this.locked = false;
35206     },
35207
35208     /**
35209      * Returns true if the selections are locked.
35210      * @return {Boolean}
35211      */
35212     isLocked : function(){
35213         return this.locked;
35214     }
35215 });/*
35216  * Based on:
35217  * Ext JS Library 1.1.1
35218  * Copyright(c) 2006-2007, Ext JS, LLC.
35219  *
35220  * Originally Released Under LGPL - original licence link has changed is not relivant.
35221  *
35222  * Fork - LGPL
35223  * <script type="text/javascript">
35224  */
35225 /**
35226  * @extends Roo.grid.AbstractSelectionModel
35227  * @class Roo.grid.RowSelectionModel
35228  * The default SelectionModel used by {@link Roo.grid.Grid}.
35229  * It supports multiple selections and keyboard selection/navigation. 
35230  * @constructor
35231  * @param {Object} config
35232  */
35233 Roo.grid.RowSelectionModel = function(config){
35234     Roo.apply(this, config);
35235     this.selections = new Roo.util.MixedCollection(false, function(o){
35236         return o.id;
35237     });
35238
35239     this.last = false;
35240     this.lastActive = false;
35241
35242     this.addEvents({
35243         /**
35244              * @event selectionchange
35245              * Fires when the selection changes
35246              * @param {SelectionModel} this
35247              */
35248             "selectionchange" : true,
35249         /**
35250              * @event afterselectionchange
35251              * Fires after the selection changes (eg. by key press or clicking)
35252              * @param {SelectionModel} this
35253              */
35254             "afterselectionchange" : true,
35255         /**
35256              * @event beforerowselect
35257              * Fires when a row is selected being selected, return false to cancel.
35258              * @param {SelectionModel} this
35259              * @param {Number} rowIndex The selected index
35260              * @param {Boolean} keepExisting False if other selections will be cleared
35261              */
35262             "beforerowselect" : true,
35263         /**
35264              * @event rowselect
35265              * Fires when a row is selected.
35266              * @param {SelectionModel} this
35267              * @param {Number} rowIndex The selected index
35268              * @param {Roo.data.Record} r The record
35269              */
35270             "rowselect" : true,
35271         /**
35272              * @event rowdeselect
35273              * Fires when a row is deselected.
35274              * @param {SelectionModel} this
35275              * @param {Number} rowIndex The selected index
35276              */
35277         "rowdeselect" : true
35278     });
35279     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35280     this.locked = false;
35281 };
35282
35283 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35284     /**
35285      * @cfg {Boolean} singleSelect
35286      * True to allow selection of only one row at a time (defaults to false)
35287      */
35288     singleSelect : false,
35289
35290     // private
35291     initEvents : function(){
35292
35293         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35294             this.grid.on("mousedown", this.handleMouseDown, this);
35295         }else{ // allow click to work like normal
35296             this.grid.on("rowclick", this.handleDragableRowClick, this);
35297         }
35298
35299         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35300             "up" : function(e){
35301                 if(!e.shiftKey){
35302                     this.selectPrevious(e.shiftKey);
35303                 }else if(this.last !== false && this.lastActive !== false){
35304                     var last = this.last;
35305                     this.selectRange(this.last,  this.lastActive-1);
35306                     this.grid.getView().focusRow(this.lastActive);
35307                     if(last !== false){
35308                         this.last = last;
35309                     }
35310                 }else{
35311                     this.selectFirstRow();
35312                 }
35313                 this.fireEvent("afterselectionchange", this);
35314             },
35315             "down" : function(e){
35316                 if(!e.shiftKey){
35317                     this.selectNext(e.shiftKey);
35318                 }else if(this.last !== false && this.lastActive !== false){
35319                     var last = this.last;
35320                     this.selectRange(this.last,  this.lastActive+1);
35321                     this.grid.getView().focusRow(this.lastActive);
35322                     if(last !== false){
35323                         this.last = last;
35324                     }
35325                 }else{
35326                     this.selectFirstRow();
35327                 }
35328                 this.fireEvent("afterselectionchange", this);
35329             },
35330             scope: this
35331         });
35332
35333         var view = this.grid.view;
35334         view.on("refresh", this.onRefresh, this);
35335         view.on("rowupdated", this.onRowUpdated, this);
35336         view.on("rowremoved", this.onRemove, this);
35337     },
35338
35339     // private
35340     onRefresh : function(){
35341         var ds = this.grid.dataSource, i, v = this.grid.view;
35342         var s = this.selections;
35343         s.each(function(r){
35344             if((i = ds.indexOfId(r.id)) != -1){
35345                 v.onRowSelect(i);
35346                 s.add(ds.getAt(i)); // updating the selection relate data
35347             }else{
35348                 s.remove(r);
35349             }
35350         });
35351     },
35352
35353     // private
35354     onRemove : function(v, index, r){
35355         this.selections.remove(r);
35356     },
35357
35358     // private
35359     onRowUpdated : function(v, index, r){
35360         if(this.isSelected(r)){
35361             v.onRowSelect(index);
35362         }
35363     },
35364
35365     /**
35366      * Select records.
35367      * @param {Array} records The records to select
35368      * @param {Boolean} keepExisting (optional) True to keep existing selections
35369      */
35370     selectRecords : function(records, keepExisting){
35371         if(!keepExisting){
35372             this.clearSelections();
35373         }
35374         var ds = this.grid.dataSource;
35375         for(var i = 0, len = records.length; i < len; i++){
35376             this.selectRow(ds.indexOf(records[i]), true);
35377         }
35378     },
35379
35380     /**
35381      * Gets the number of selected rows.
35382      * @return {Number}
35383      */
35384     getCount : function(){
35385         return this.selections.length;
35386     },
35387
35388     /**
35389      * Selects the first row in the grid.
35390      */
35391     selectFirstRow : function(){
35392         this.selectRow(0);
35393     },
35394
35395     /**
35396      * Select the last row.
35397      * @param {Boolean} keepExisting (optional) True to keep existing selections
35398      */
35399     selectLastRow : function(keepExisting){
35400         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
35401     },
35402
35403     /**
35404      * Selects the row immediately following the last selected row.
35405      * @param {Boolean} keepExisting (optional) True to keep existing selections
35406      */
35407     selectNext : function(keepExisting){
35408         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
35409             this.selectRow(this.last+1, keepExisting);
35410             this.grid.getView().focusRow(this.last);
35411         }
35412     },
35413
35414     /**
35415      * Selects the row that precedes the last selected row.
35416      * @param {Boolean} keepExisting (optional) True to keep existing selections
35417      */
35418     selectPrevious : function(keepExisting){
35419         if(this.last){
35420             this.selectRow(this.last-1, keepExisting);
35421             this.grid.getView().focusRow(this.last);
35422         }
35423     },
35424
35425     /**
35426      * Returns the selected records
35427      * @return {Array} Array of selected records
35428      */
35429     getSelections : function(){
35430         return [].concat(this.selections.items);
35431     },
35432
35433     /**
35434      * Returns the first selected record.
35435      * @return {Record}
35436      */
35437     getSelected : function(){
35438         return this.selections.itemAt(0);
35439     },
35440
35441
35442     /**
35443      * Clears all selections.
35444      */
35445     clearSelections : function(fast){
35446         if(this.locked) {
35447             return;
35448         }
35449         if(fast !== true){
35450             var ds = this.grid.dataSource;
35451             var s = this.selections;
35452             s.each(function(r){
35453                 this.deselectRow(ds.indexOfId(r.id));
35454             }, this);
35455             s.clear();
35456         }else{
35457             this.selections.clear();
35458         }
35459         this.last = false;
35460     },
35461
35462
35463     /**
35464      * Selects all rows.
35465      */
35466     selectAll : function(){
35467         if(this.locked) {
35468             return;
35469         }
35470         this.selections.clear();
35471         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
35472             this.selectRow(i, true);
35473         }
35474     },
35475
35476     /**
35477      * Returns True if there is a selection.
35478      * @return {Boolean}
35479      */
35480     hasSelection : function(){
35481         return this.selections.length > 0;
35482     },
35483
35484     /**
35485      * Returns True if the specified row is selected.
35486      * @param {Number/Record} record The record or index of the record to check
35487      * @return {Boolean}
35488      */
35489     isSelected : function(index){
35490         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
35491         return (r && this.selections.key(r.id) ? true : false);
35492     },
35493
35494     /**
35495      * Returns True if the specified record id is selected.
35496      * @param {String} id The id of record to check
35497      * @return {Boolean}
35498      */
35499     isIdSelected : function(id){
35500         return (this.selections.key(id) ? true : false);
35501     },
35502
35503     // private
35504     handleMouseDown : function(e, t){
35505         var view = this.grid.getView(), rowIndex;
35506         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
35507             return;
35508         };
35509         if(e.shiftKey && this.last !== false){
35510             var last = this.last;
35511             this.selectRange(last, rowIndex, e.ctrlKey);
35512             this.last = last; // reset the last
35513             view.focusRow(rowIndex);
35514         }else{
35515             var isSelected = this.isSelected(rowIndex);
35516             if(e.button !== 0 && isSelected){
35517                 view.focusRow(rowIndex);
35518             }else if(e.ctrlKey && isSelected){
35519                 this.deselectRow(rowIndex);
35520             }else if(!isSelected){
35521                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
35522                 view.focusRow(rowIndex);
35523             }
35524         }
35525         this.fireEvent("afterselectionchange", this);
35526     },
35527     // private
35528     handleDragableRowClick :  function(grid, rowIndex, e) 
35529     {
35530         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
35531             this.selectRow(rowIndex, false);
35532             grid.view.focusRow(rowIndex);
35533              this.fireEvent("afterselectionchange", this);
35534         }
35535     },
35536     
35537     /**
35538      * Selects multiple rows.
35539      * @param {Array} rows Array of the indexes of the row to select
35540      * @param {Boolean} keepExisting (optional) True to keep existing selections
35541      */
35542     selectRows : function(rows, keepExisting){
35543         if(!keepExisting){
35544             this.clearSelections();
35545         }
35546         for(var i = 0, len = rows.length; i < len; i++){
35547             this.selectRow(rows[i], true);
35548         }
35549     },
35550
35551     /**
35552      * Selects a range of rows. All rows in between startRow and endRow are also selected.
35553      * @param {Number} startRow The index of the first row in the range
35554      * @param {Number} endRow The index of the last row in the range
35555      * @param {Boolean} keepExisting (optional) True to retain existing selections
35556      */
35557     selectRange : function(startRow, endRow, keepExisting){
35558         if(this.locked) {
35559             return;
35560         }
35561         if(!keepExisting){
35562             this.clearSelections();
35563         }
35564         if(startRow <= endRow){
35565             for(var i = startRow; i <= endRow; i++){
35566                 this.selectRow(i, true);
35567             }
35568         }else{
35569             for(var i = startRow; i >= endRow; i--){
35570                 this.selectRow(i, true);
35571             }
35572         }
35573     },
35574
35575     /**
35576      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
35577      * @param {Number} startRow The index of the first row in the range
35578      * @param {Number} endRow The index of the last row in the range
35579      */
35580     deselectRange : function(startRow, endRow, preventViewNotify){
35581         if(this.locked) {
35582             return;
35583         }
35584         for(var i = startRow; i <= endRow; i++){
35585             this.deselectRow(i, preventViewNotify);
35586         }
35587     },
35588
35589     /**
35590      * Selects a row.
35591      * @param {Number} row The index of the row to select
35592      * @param {Boolean} keepExisting (optional) True to keep existing selections
35593      */
35594     selectRow : function(index, keepExisting, preventViewNotify){
35595         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
35596             return;
35597         }
35598         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
35599             if(!keepExisting || this.singleSelect){
35600                 this.clearSelections();
35601             }
35602             var r = this.grid.dataSource.getAt(index);
35603             this.selections.add(r);
35604             this.last = this.lastActive = index;
35605             if(!preventViewNotify){
35606                 this.grid.getView().onRowSelect(index);
35607             }
35608             this.fireEvent("rowselect", this, index, r);
35609             this.fireEvent("selectionchange", this);
35610         }
35611     },
35612
35613     /**
35614      * Deselects a row.
35615      * @param {Number} row The index of the row to deselect
35616      */
35617     deselectRow : function(index, preventViewNotify){
35618         if(this.locked) {
35619             return;
35620         }
35621         if(this.last == index){
35622             this.last = false;
35623         }
35624         if(this.lastActive == index){
35625             this.lastActive = false;
35626         }
35627         var r = this.grid.dataSource.getAt(index);
35628         this.selections.remove(r);
35629         if(!preventViewNotify){
35630             this.grid.getView().onRowDeselect(index);
35631         }
35632         this.fireEvent("rowdeselect", this, index);
35633         this.fireEvent("selectionchange", this);
35634     },
35635
35636     // private
35637     restoreLast : function(){
35638         if(this._last){
35639             this.last = this._last;
35640         }
35641     },
35642
35643     // private
35644     acceptsNav : function(row, col, cm){
35645         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35646     },
35647
35648     // private
35649     onEditorKey : function(field, e){
35650         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35651         if(k == e.TAB){
35652             e.stopEvent();
35653             ed.completeEdit();
35654             if(e.shiftKey){
35655                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35656             }else{
35657                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35658             }
35659         }else if(k == e.ENTER && !e.ctrlKey){
35660             e.stopEvent();
35661             ed.completeEdit();
35662             if(e.shiftKey){
35663                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
35664             }else{
35665                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
35666             }
35667         }else if(k == e.ESC){
35668             ed.cancelEdit();
35669         }
35670         if(newCell){
35671             g.startEditing(newCell[0], newCell[1]);
35672         }
35673     }
35674 });/*
35675  * Based on:
35676  * Ext JS Library 1.1.1
35677  * Copyright(c) 2006-2007, Ext JS, LLC.
35678  *
35679  * Originally Released Under LGPL - original licence link has changed is not relivant.
35680  *
35681  * Fork - LGPL
35682  * <script type="text/javascript">
35683  */
35684 /**
35685  * @class Roo.grid.CellSelectionModel
35686  * @extends Roo.grid.AbstractSelectionModel
35687  * This class provides the basic implementation for cell selection in a grid.
35688  * @constructor
35689  * @param {Object} config The object containing the configuration of this model.
35690  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
35691  */
35692 Roo.grid.CellSelectionModel = function(config){
35693     Roo.apply(this, config);
35694
35695     this.selection = null;
35696
35697     this.addEvents({
35698         /**
35699              * @event beforerowselect
35700              * Fires before a cell is selected.
35701              * @param {SelectionModel} this
35702              * @param {Number} rowIndex The selected row index
35703              * @param {Number} colIndex The selected cell index
35704              */
35705             "beforecellselect" : true,
35706         /**
35707              * @event cellselect
35708              * Fires when a cell is selected.
35709              * @param {SelectionModel} this
35710              * @param {Number} rowIndex The selected row index
35711              * @param {Number} colIndex The selected cell index
35712              */
35713             "cellselect" : true,
35714         /**
35715              * @event selectionchange
35716              * Fires when the active selection changes.
35717              * @param {SelectionModel} this
35718              * @param {Object} selection null for no selection or an object (o) with two properties
35719                 <ul>
35720                 <li>o.record: the record object for the row the selection is in</li>
35721                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
35722                 </ul>
35723              */
35724             "selectionchange" : true,
35725         /**
35726              * @event tabend
35727              * Fires when the tab (or enter) was pressed on the last editable cell
35728              * You can use this to trigger add new row.
35729              * @param {SelectionModel} this
35730              */
35731             "tabend" : true,
35732          /**
35733              * @event beforeeditnext
35734              * Fires before the next editable sell is made active
35735              * You can use this to skip to another cell or fire the tabend
35736              *    if you set cell to false
35737              * @param {Object} eventdata object : { cell : [ row, col ] } 
35738              */
35739             "beforeeditnext" : true
35740     });
35741     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
35742 };
35743
35744 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
35745     
35746     enter_is_tab: false,
35747
35748     /** @ignore */
35749     initEvents : function(){
35750         this.grid.on("mousedown", this.handleMouseDown, this);
35751         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
35752         var view = this.grid.view;
35753         view.on("refresh", this.onViewChange, this);
35754         view.on("rowupdated", this.onRowUpdated, this);
35755         view.on("beforerowremoved", this.clearSelections, this);
35756         view.on("beforerowsinserted", this.clearSelections, this);
35757         if(this.grid.isEditor){
35758             this.grid.on("beforeedit", this.beforeEdit,  this);
35759         }
35760     },
35761
35762         //private
35763     beforeEdit : function(e){
35764         this.select(e.row, e.column, false, true, e.record);
35765     },
35766
35767         //private
35768     onRowUpdated : function(v, index, r){
35769         if(this.selection && this.selection.record == r){
35770             v.onCellSelect(index, this.selection.cell[1]);
35771         }
35772     },
35773
35774         //private
35775     onViewChange : function(){
35776         this.clearSelections(true);
35777     },
35778
35779         /**
35780          * Returns the currently selected cell,.
35781          * @return {Array} The selected cell (row, column) or null if none selected.
35782          */
35783     getSelectedCell : function(){
35784         return this.selection ? this.selection.cell : null;
35785     },
35786
35787     /**
35788      * Clears all selections.
35789      * @param {Boolean} true to prevent the gridview from being notified about the change.
35790      */
35791     clearSelections : function(preventNotify){
35792         var s = this.selection;
35793         if(s){
35794             if(preventNotify !== true){
35795                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
35796             }
35797             this.selection = null;
35798             this.fireEvent("selectionchange", this, null);
35799         }
35800     },
35801
35802     /**
35803      * Returns true if there is a selection.
35804      * @return {Boolean}
35805      */
35806     hasSelection : function(){
35807         return this.selection ? true : false;
35808     },
35809
35810     /** @ignore */
35811     handleMouseDown : function(e, t){
35812         var v = this.grid.getView();
35813         if(this.isLocked()){
35814             return;
35815         };
35816         var row = v.findRowIndex(t);
35817         var cell = v.findCellIndex(t);
35818         if(row !== false && cell !== false){
35819             this.select(row, cell);
35820         }
35821     },
35822
35823     /**
35824      * Selects a cell.
35825      * @param {Number} rowIndex
35826      * @param {Number} collIndex
35827      */
35828     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
35829         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
35830             this.clearSelections();
35831             r = r || this.grid.dataSource.getAt(rowIndex);
35832             this.selection = {
35833                 record : r,
35834                 cell : [rowIndex, colIndex]
35835             };
35836             if(!preventViewNotify){
35837                 var v = this.grid.getView();
35838                 v.onCellSelect(rowIndex, colIndex);
35839                 if(preventFocus !== true){
35840                     v.focusCell(rowIndex, colIndex);
35841                 }
35842             }
35843             this.fireEvent("cellselect", this, rowIndex, colIndex);
35844             this.fireEvent("selectionchange", this, this.selection);
35845         }
35846     },
35847
35848         //private
35849     isSelectable : function(rowIndex, colIndex, cm){
35850         return !cm.isHidden(colIndex);
35851     },
35852
35853     /** @ignore */
35854     handleKeyDown : function(e){
35855         //Roo.log('Cell Sel Model handleKeyDown');
35856         if(!e.isNavKeyPress()){
35857             return;
35858         }
35859         var g = this.grid, s = this.selection;
35860         if(!s){
35861             e.stopEvent();
35862             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
35863             if(cell){
35864                 this.select(cell[0], cell[1]);
35865             }
35866             return;
35867         }
35868         var sm = this;
35869         var walk = function(row, col, step){
35870             return g.walkCells(row, col, step, sm.isSelectable,  sm);
35871         };
35872         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
35873         var newCell;
35874
35875       
35876
35877         switch(k){
35878             case e.TAB:
35879                 // handled by onEditorKey
35880                 if (g.isEditor && g.editing) {
35881                     return;
35882                 }
35883                 if(e.shiftKey) {
35884                     newCell = walk(r, c-1, -1);
35885                 } else {
35886                     newCell = walk(r, c+1, 1);
35887                 }
35888                 break;
35889             
35890             case e.DOWN:
35891                newCell = walk(r+1, c, 1);
35892                 break;
35893             
35894             case e.UP:
35895                 newCell = walk(r-1, c, -1);
35896                 break;
35897             
35898             case e.RIGHT:
35899                 newCell = walk(r, c+1, 1);
35900                 break;
35901             
35902             case e.LEFT:
35903                 newCell = walk(r, c-1, -1);
35904                 break;
35905             
35906             case e.ENTER:
35907                 
35908                 if(g.isEditor && !g.editing){
35909                    g.startEditing(r, c);
35910                    e.stopEvent();
35911                    return;
35912                 }
35913                 
35914                 
35915              break;
35916         };
35917         if(newCell){
35918             this.select(newCell[0], newCell[1]);
35919             e.stopEvent();
35920             
35921         }
35922     },
35923
35924     acceptsNav : function(row, col, cm){
35925         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35926     },
35927     /**
35928      * Selects a cell.
35929      * @param {Number} field (not used) - as it's normally used as a listener
35930      * @param {Number} e - event - fake it by using
35931      *
35932      * var e = Roo.EventObjectImpl.prototype;
35933      * e.keyCode = e.TAB
35934      *
35935      * 
35936      */
35937     onEditorKey : function(field, e){
35938         
35939         var k = e.getKey(),
35940             newCell,
35941             g = this.grid,
35942             ed = g.activeEditor,
35943             forward = false;
35944         ///Roo.log('onEditorKey' + k);
35945         
35946         
35947         if (this.enter_is_tab && k == e.ENTER) {
35948             k = e.TAB;
35949         }
35950         
35951         if(k == e.TAB){
35952             if(e.shiftKey){
35953                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35954             }else{
35955                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35956                 forward = true;
35957             }
35958             
35959             e.stopEvent();
35960             
35961         } else if(k == e.ENTER &&  !e.ctrlKey){
35962             ed.completeEdit();
35963             e.stopEvent();
35964             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35965         
35966                 } else if(k == e.ESC){
35967             ed.cancelEdit();
35968         }
35969                 
35970         if (newCell) {
35971             var ecall = { cell : newCell, forward : forward };
35972             this.fireEvent('beforeeditnext', ecall );
35973             newCell = ecall.cell;
35974                         forward = ecall.forward;
35975         }
35976                 
35977         if(newCell){
35978             //Roo.log('next cell after edit');
35979             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
35980         } else if (forward) {
35981             // tabbed past last
35982             this.fireEvent.defer(100, this, ['tabend',this]);
35983         }
35984     }
35985 });/*
35986  * Based on:
35987  * Ext JS Library 1.1.1
35988  * Copyright(c) 2006-2007, Ext JS, LLC.
35989  *
35990  * Originally Released Under LGPL - original licence link has changed is not relivant.
35991  *
35992  * Fork - LGPL
35993  * <script type="text/javascript">
35994  */
35995  
35996 /**
35997  * @class Roo.grid.EditorGrid
35998  * @extends Roo.grid.Grid
35999  * Class for creating and editable grid.
36000  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36001  * The container MUST have some type of size defined for the grid to fill. The container will be 
36002  * automatically set to position relative if it isn't already.
36003  * @param {Object} dataSource The data model to bind to
36004  * @param {Object} colModel The column model with info about this grid's columns
36005  */
36006 Roo.grid.EditorGrid = function(container, config){
36007     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36008     this.getGridEl().addClass("xedit-grid");
36009
36010     if(!this.selModel){
36011         this.selModel = new Roo.grid.CellSelectionModel();
36012     }
36013
36014     this.activeEditor = null;
36015
36016         this.addEvents({
36017             /**
36018              * @event beforeedit
36019              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36020              * <ul style="padding:5px;padding-left:16px;">
36021              * <li>grid - This grid</li>
36022              * <li>record - The record being edited</li>
36023              * <li>field - The field name being edited</li>
36024              * <li>value - The value for the field being edited.</li>
36025              * <li>row - The grid row index</li>
36026              * <li>column - The grid column index</li>
36027              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36028              * </ul>
36029              * @param {Object} e An edit event (see above for description)
36030              */
36031             "beforeedit" : true,
36032             /**
36033              * @event afteredit
36034              * Fires after a cell is edited. <br />
36035              * <ul style="padding:5px;padding-left:16px;">
36036              * <li>grid - This grid</li>
36037              * <li>record - The record being edited</li>
36038              * <li>field - The field name being edited</li>
36039              * <li>value - The value being set</li>
36040              * <li>originalValue - The original value for the field, before the edit.</li>
36041              * <li>row - The grid row index</li>
36042              * <li>column - The grid column index</li>
36043              * </ul>
36044              * @param {Object} e An edit event (see above for description)
36045              */
36046             "afteredit" : true,
36047             /**
36048              * @event validateedit
36049              * Fires after a cell is edited, but before the value is set in the record. 
36050          * You can use this to modify the value being set in the field, Return false
36051              * to cancel the change. The edit event object has the following properties <br />
36052              * <ul style="padding:5px;padding-left:16px;">
36053          * <li>editor - This editor</li>
36054              * <li>grid - This grid</li>
36055              * <li>record - The record being edited</li>
36056              * <li>field - The field name being edited</li>
36057              * <li>value - The value being set</li>
36058              * <li>originalValue - The original value for the field, before the edit.</li>
36059              * <li>row - The grid row index</li>
36060              * <li>column - The grid column index</li>
36061              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36062              * </ul>
36063              * @param {Object} e An edit event (see above for description)
36064              */
36065             "validateedit" : true
36066         });
36067     this.on("bodyscroll", this.stopEditing,  this);
36068     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36069 };
36070
36071 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36072     /**
36073      * @cfg {Number} clicksToEdit
36074      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36075      */
36076     clicksToEdit: 2,
36077
36078     // private
36079     isEditor : true,
36080     // private
36081     trackMouseOver: false, // causes very odd FF errors
36082
36083     onCellDblClick : function(g, row, col){
36084         this.startEditing(row, col);
36085     },
36086
36087     onEditComplete : function(ed, value, startValue){
36088         this.editing = false;
36089         this.activeEditor = null;
36090         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36091         var r = ed.record;
36092         var field = this.colModel.getDataIndex(ed.col);
36093         var e = {
36094             grid: this,
36095             record: r,
36096             field: field,
36097             originalValue: startValue,
36098             value: value,
36099             row: ed.row,
36100             column: ed.col,
36101             cancel:false,
36102             editor: ed
36103         };
36104         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36105         cell.show();
36106           
36107         if(String(value) !== String(startValue)){
36108             
36109             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36110                 r.set(field, e.value);
36111                 // if we are dealing with a combo box..
36112                 // then we also set the 'name' colum to be the displayField
36113                 if (ed.field.displayField && ed.field.name) {
36114                     r.set(ed.field.name, ed.field.el.dom.value);
36115                 }
36116                 
36117                 delete e.cancel; //?? why!!!
36118                 this.fireEvent("afteredit", e);
36119             }
36120         } else {
36121             this.fireEvent("afteredit", e); // always fire it!
36122         }
36123         this.view.focusCell(ed.row, ed.col);
36124     },
36125
36126     /**
36127      * Starts editing the specified for the specified row/column
36128      * @param {Number} rowIndex
36129      * @param {Number} colIndex
36130      */
36131     startEditing : function(row, col){
36132         this.stopEditing();
36133         if(this.colModel.isCellEditable(col, row)){
36134             this.view.ensureVisible(row, col, true);
36135           
36136             var r = this.dataSource.getAt(row);
36137             var field = this.colModel.getDataIndex(col);
36138             var cell = Roo.get(this.view.getCell(row,col));
36139             var e = {
36140                 grid: this,
36141                 record: r,
36142                 field: field,
36143                 value: r.data[field],
36144                 row: row,
36145                 column: col,
36146                 cancel:false 
36147             };
36148             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36149                 this.editing = true;
36150                 var ed = this.colModel.getCellEditor(col, row);
36151                 
36152                 if (!ed) {
36153                     return;
36154                 }
36155                 if(!ed.rendered){
36156                     ed.render(ed.parentEl || document.body);
36157                 }
36158                 ed.field.reset();
36159                
36160                 cell.hide();
36161                 
36162                 (function(){ // complex but required for focus issues in safari, ie and opera
36163                     ed.row = row;
36164                     ed.col = col;
36165                     ed.record = r;
36166                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
36167                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
36168                     this.activeEditor = ed;
36169                     var v = r.data[field];
36170                     ed.startEdit(this.view.getCell(row, col), v);
36171                     // combo's with 'displayField and name set
36172                     if (ed.field.displayField && ed.field.name) {
36173                         ed.field.el.dom.value = r.data[ed.field.name];
36174                     }
36175                     
36176                     
36177                 }).defer(50, this);
36178             }
36179         }
36180     },
36181         
36182     /**
36183      * Stops any active editing
36184      */
36185     stopEditing : function(){
36186         if(this.activeEditor){
36187             this.activeEditor.completeEdit();
36188         }
36189         this.activeEditor = null;
36190     },
36191         
36192          /**
36193      * Called to get grid's drag proxy text, by default returns this.ddText.
36194      * @return {String}
36195      */
36196     getDragDropText : function(){
36197         var count = this.selModel.getSelectedCell() ? 1 : 0;
36198         return String.format(this.ddText, count, count == 1 ? '' : 's');
36199     }
36200         
36201 });/*
36202  * Based on:
36203  * Ext JS Library 1.1.1
36204  * Copyright(c) 2006-2007, Ext JS, LLC.
36205  *
36206  * Originally Released Under LGPL - original licence link has changed is not relivant.
36207  *
36208  * Fork - LGPL
36209  * <script type="text/javascript">
36210  */
36211
36212 // private - not really -- you end up using it !
36213 // This is a support class used internally by the Grid components
36214
36215 /**
36216  * @class Roo.grid.GridEditor
36217  * @extends Roo.Editor
36218  * Class for creating and editable grid elements.
36219  * @param {Object} config any settings (must include field)
36220  */
36221 Roo.grid.GridEditor = function(field, config){
36222     if (!config && field.field) {
36223         config = field;
36224         field = Roo.factory(config.field, Roo.form);
36225     }
36226     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36227     field.monitorTab = false;
36228 };
36229
36230 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36231     
36232     /**
36233      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36234      */
36235     
36236     alignment: "tl-tl",
36237     autoSize: "width",
36238     hideEl : false,
36239     cls: "x-small-editor x-grid-editor",
36240     shim:false,
36241     shadow:"frame"
36242 });/*
36243  * Based on:
36244  * Ext JS Library 1.1.1
36245  * Copyright(c) 2006-2007, Ext JS, LLC.
36246  *
36247  * Originally Released Under LGPL - original licence link has changed is not relivant.
36248  *
36249  * Fork - LGPL
36250  * <script type="text/javascript">
36251  */
36252   
36253
36254   
36255 Roo.grid.PropertyRecord = Roo.data.Record.create([
36256     {name:'name',type:'string'},  'value'
36257 ]);
36258
36259
36260 Roo.grid.PropertyStore = function(grid, source){
36261     this.grid = grid;
36262     this.store = new Roo.data.Store({
36263         recordType : Roo.grid.PropertyRecord
36264     });
36265     this.store.on('update', this.onUpdate,  this);
36266     if(source){
36267         this.setSource(source);
36268     }
36269     Roo.grid.PropertyStore.superclass.constructor.call(this);
36270 };
36271
36272
36273
36274 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36275     setSource : function(o){
36276         this.source = o;
36277         this.store.removeAll();
36278         var data = [];
36279         for(var k in o){
36280             if(this.isEditableValue(o[k])){
36281                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36282             }
36283         }
36284         this.store.loadRecords({records: data}, {}, true);
36285     },
36286
36287     onUpdate : function(ds, record, type){
36288         if(type == Roo.data.Record.EDIT){
36289             var v = record.data['value'];
36290             var oldValue = record.modified['value'];
36291             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36292                 this.source[record.id] = v;
36293                 record.commit();
36294                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36295             }else{
36296                 record.reject();
36297             }
36298         }
36299     },
36300
36301     getProperty : function(row){
36302        return this.store.getAt(row);
36303     },
36304
36305     isEditableValue: function(val){
36306         if(val && val instanceof Date){
36307             return true;
36308         }else if(typeof val == 'object' || typeof val == 'function'){
36309             return false;
36310         }
36311         return true;
36312     },
36313
36314     setValue : function(prop, value){
36315         this.source[prop] = value;
36316         this.store.getById(prop).set('value', value);
36317     },
36318
36319     getSource : function(){
36320         return this.source;
36321     }
36322 });
36323
36324 Roo.grid.PropertyColumnModel = function(grid, store){
36325     this.grid = grid;
36326     var g = Roo.grid;
36327     g.PropertyColumnModel.superclass.constructor.call(this, [
36328         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36329         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36330     ]);
36331     this.store = store;
36332     this.bselect = Roo.DomHelper.append(document.body, {
36333         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36334             {tag: 'option', value: 'true', html: 'true'},
36335             {tag: 'option', value: 'false', html: 'false'}
36336         ]
36337     });
36338     Roo.id(this.bselect);
36339     var f = Roo.form;
36340     this.editors = {
36341         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36342         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36343         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36344         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36345         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36346     };
36347     this.renderCellDelegate = this.renderCell.createDelegate(this);
36348     this.renderPropDelegate = this.renderProp.createDelegate(this);
36349 };
36350
36351 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36352     
36353     
36354     nameText : 'Name',
36355     valueText : 'Value',
36356     
36357     dateFormat : 'm/j/Y',
36358     
36359     
36360     renderDate : function(dateVal){
36361         return dateVal.dateFormat(this.dateFormat);
36362     },
36363
36364     renderBool : function(bVal){
36365         return bVal ? 'true' : 'false';
36366     },
36367
36368     isCellEditable : function(colIndex, rowIndex){
36369         return colIndex == 1;
36370     },
36371
36372     getRenderer : function(col){
36373         return col == 1 ?
36374             this.renderCellDelegate : this.renderPropDelegate;
36375     },
36376
36377     renderProp : function(v){
36378         return this.getPropertyName(v);
36379     },
36380
36381     renderCell : function(val){
36382         var rv = val;
36383         if(val instanceof Date){
36384             rv = this.renderDate(val);
36385         }else if(typeof val == 'boolean'){
36386             rv = this.renderBool(val);
36387         }
36388         return Roo.util.Format.htmlEncode(rv);
36389     },
36390
36391     getPropertyName : function(name){
36392         var pn = this.grid.propertyNames;
36393         return pn && pn[name] ? pn[name] : name;
36394     },
36395
36396     getCellEditor : function(colIndex, rowIndex){
36397         var p = this.store.getProperty(rowIndex);
36398         var n = p.data['name'], val = p.data['value'];
36399         
36400         if(typeof(this.grid.customEditors[n]) == 'string'){
36401             return this.editors[this.grid.customEditors[n]];
36402         }
36403         if(typeof(this.grid.customEditors[n]) != 'undefined'){
36404             return this.grid.customEditors[n];
36405         }
36406         if(val instanceof Date){
36407             return this.editors['date'];
36408         }else if(typeof val == 'number'){
36409             return this.editors['number'];
36410         }else if(typeof val == 'boolean'){
36411             return this.editors['boolean'];
36412         }else{
36413             return this.editors['string'];
36414         }
36415     }
36416 });
36417
36418 /**
36419  * @class Roo.grid.PropertyGrid
36420  * @extends Roo.grid.EditorGrid
36421  * This class represents the  interface of a component based property grid control.
36422  * <br><br>Usage:<pre><code>
36423  var grid = new Roo.grid.PropertyGrid("my-container-id", {
36424       
36425  });
36426  // set any options
36427  grid.render();
36428  * </code></pre>
36429   
36430  * @constructor
36431  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36432  * The container MUST have some type of size defined for the grid to fill. The container will be
36433  * automatically set to position relative if it isn't already.
36434  * @param {Object} config A config object that sets properties on this grid.
36435  */
36436 Roo.grid.PropertyGrid = function(container, config){
36437     config = config || {};
36438     var store = new Roo.grid.PropertyStore(this);
36439     this.store = store;
36440     var cm = new Roo.grid.PropertyColumnModel(this, store);
36441     store.store.sort('name', 'ASC');
36442     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
36443         ds: store.store,
36444         cm: cm,
36445         enableColLock:false,
36446         enableColumnMove:false,
36447         stripeRows:false,
36448         trackMouseOver: false,
36449         clicksToEdit:1
36450     }, config));
36451     this.getGridEl().addClass('x-props-grid');
36452     this.lastEditRow = null;
36453     this.on('columnresize', this.onColumnResize, this);
36454     this.addEvents({
36455          /**
36456              * @event beforepropertychange
36457              * Fires before a property changes (return false to stop?)
36458              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36459              * @param {String} id Record Id
36460              * @param {String} newval New Value
36461          * @param {String} oldval Old Value
36462              */
36463         "beforepropertychange": true,
36464         /**
36465              * @event propertychange
36466              * Fires after a property changes
36467              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36468              * @param {String} id Record Id
36469              * @param {String} newval New Value
36470          * @param {String} oldval Old Value
36471              */
36472         "propertychange": true
36473     });
36474     this.customEditors = this.customEditors || {};
36475 };
36476 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
36477     
36478      /**
36479      * @cfg {Object} customEditors map of colnames=> custom editors.
36480      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
36481      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
36482      * false disables editing of the field.
36483          */
36484     
36485       /**
36486      * @cfg {Object} propertyNames map of property Names to their displayed value
36487          */
36488     
36489     render : function(){
36490         Roo.grid.PropertyGrid.superclass.render.call(this);
36491         this.autoSize.defer(100, this);
36492     },
36493
36494     autoSize : function(){
36495         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
36496         if(this.view){
36497             this.view.fitColumns();
36498         }
36499     },
36500
36501     onColumnResize : function(){
36502         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
36503         this.autoSize();
36504     },
36505     /**
36506      * Sets the data for the Grid
36507      * accepts a Key => Value object of all the elements avaiable.
36508      * @param {Object} data  to appear in grid.
36509      */
36510     setSource : function(source){
36511         this.store.setSource(source);
36512         //this.autoSize();
36513     },
36514     /**
36515      * Gets all the data from the grid.
36516      * @return {Object} data  data stored in grid
36517      */
36518     getSource : function(){
36519         return this.store.getSource();
36520     }
36521 });/*
36522   
36523  * Licence LGPL
36524  
36525  */
36526  
36527 /**
36528  * @class Roo.grid.Calendar
36529  * @extends Roo.util.Grid
36530  * This class extends the Grid to provide a calendar widget
36531  * <br><br>Usage:<pre><code>
36532  var grid = new Roo.grid.Calendar("my-container-id", {
36533      ds: myDataStore,
36534      cm: myColModel,
36535      selModel: mySelectionModel,
36536      autoSizeColumns: true,
36537      monitorWindowResize: false,
36538      trackMouseOver: true
36539      eventstore : real data store..
36540  });
36541  // set any options
36542  grid.render();
36543   
36544   * @constructor
36545  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36546  * The container MUST have some type of size defined for the grid to fill. The container will be
36547  * automatically set to position relative if it isn't already.
36548  * @param {Object} config A config object that sets properties on this grid.
36549  */
36550 Roo.grid.Calendar = function(container, config){
36551         // initialize the container
36552         this.container = Roo.get(container);
36553         this.container.update("");
36554         this.container.setStyle("overflow", "hidden");
36555     this.container.addClass('x-grid-container');
36556
36557     this.id = this.container.id;
36558
36559     Roo.apply(this, config);
36560     // check and correct shorthanded configs
36561     
36562     var rows = [];
36563     var d =1;
36564     for (var r = 0;r < 6;r++) {
36565         
36566         rows[r]=[];
36567         for (var c =0;c < 7;c++) {
36568             rows[r][c]= '';
36569         }
36570     }
36571     if (this.eventStore) {
36572         this.eventStore= Roo.factory(this.eventStore, Roo.data);
36573         this.eventStore.on('load',this.onLoad, this);
36574         this.eventStore.on('beforeload',this.clearEvents, this);
36575          
36576     }
36577     
36578     this.dataSource = new Roo.data.Store({
36579             proxy: new Roo.data.MemoryProxy(rows),
36580             reader: new Roo.data.ArrayReader({}, [
36581                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
36582     });
36583
36584     this.dataSource.load();
36585     this.ds = this.dataSource;
36586     this.ds.xmodule = this.xmodule || false;
36587     
36588     
36589     var cellRender = function(v,x,r)
36590     {
36591         return String.format(
36592             '<div class="fc-day  fc-widget-content"><div>' +
36593                 '<div class="fc-event-container"></div>' +
36594                 '<div class="fc-day-number">{0}</div>'+
36595                 
36596                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
36597             '</div></div>', v);
36598     
36599     }
36600     
36601     
36602     this.colModel = new Roo.grid.ColumnModel( [
36603         {
36604             xtype: 'ColumnModel',
36605             xns: Roo.grid,
36606             dataIndex : 'weekday0',
36607             header : 'Sunday',
36608             renderer : cellRender
36609         },
36610         {
36611             xtype: 'ColumnModel',
36612             xns: Roo.grid,
36613             dataIndex : 'weekday1',
36614             header : 'Monday',
36615             renderer : cellRender
36616         },
36617         {
36618             xtype: 'ColumnModel',
36619             xns: Roo.grid,
36620             dataIndex : 'weekday2',
36621             header : 'Tuesday',
36622             renderer : cellRender
36623         },
36624         {
36625             xtype: 'ColumnModel',
36626             xns: Roo.grid,
36627             dataIndex : 'weekday3',
36628             header : 'Wednesday',
36629             renderer : cellRender
36630         },
36631         {
36632             xtype: 'ColumnModel',
36633             xns: Roo.grid,
36634             dataIndex : 'weekday4',
36635             header : 'Thursday',
36636             renderer : cellRender
36637         },
36638         {
36639             xtype: 'ColumnModel',
36640             xns: Roo.grid,
36641             dataIndex : 'weekday5',
36642             header : 'Friday',
36643             renderer : cellRender
36644         },
36645         {
36646             xtype: 'ColumnModel',
36647             xns: Roo.grid,
36648             dataIndex : 'weekday6',
36649             header : 'Saturday',
36650             renderer : cellRender
36651         }
36652     ]);
36653     this.cm = this.colModel;
36654     this.cm.xmodule = this.xmodule || false;
36655  
36656         
36657           
36658     //this.selModel = new Roo.grid.CellSelectionModel();
36659     //this.sm = this.selModel;
36660     //this.selModel.init(this);
36661     
36662     
36663     if(this.width){
36664         this.container.setWidth(this.width);
36665     }
36666
36667     if(this.height){
36668         this.container.setHeight(this.height);
36669     }
36670     /** @private */
36671         this.addEvents({
36672         // raw events
36673         /**
36674          * @event click
36675          * The raw click event for the entire grid.
36676          * @param {Roo.EventObject} e
36677          */
36678         "click" : true,
36679         /**
36680          * @event dblclick
36681          * The raw dblclick event for the entire grid.
36682          * @param {Roo.EventObject} e
36683          */
36684         "dblclick" : true,
36685         /**
36686          * @event contextmenu
36687          * The raw contextmenu event for the entire grid.
36688          * @param {Roo.EventObject} e
36689          */
36690         "contextmenu" : true,
36691         /**
36692          * @event mousedown
36693          * The raw mousedown event for the entire grid.
36694          * @param {Roo.EventObject} e
36695          */
36696         "mousedown" : true,
36697         /**
36698          * @event mouseup
36699          * The raw mouseup event for the entire grid.
36700          * @param {Roo.EventObject} e
36701          */
36702         "mouseup" : true,
36703         /**
36704          * @event mouseover
36705          * The raw mouseover event for the entire grid.
36706          * @param {Roo.EventObject} e
36707          */
36708         "mouseover" : true,
36709         /**
36710          * @event mouseout
36711          * The raw mouseout event for the entire grid.
36712          * @param {Roo.EventObject} e
36713          */
36714         "mouseout" : true,
36715         /**
36716          * @event keypress
36717          * The raw keypress event for the entire grid.
36718          * @param {Roo.EventObject} e
36719          */
36720         "keypress" : true,
36721         /**
36722          * @event keydown
36723          * The raw keydown event for the entire grid.
36724          * @param {Roo.EventObject} e
36725          */
36726         "keydown" : true,
36727
36728         // custom events
36729
36730         /**
36731          * @event cellclick
36732          * Fires when a cell is clicked
36733          * @param {Grid} this
36734          * @param {Number} rowIndex
36735          * @param {Number} columnIndex
36736          * @param {Roo.EventObject} e
36737          */
36738         "cellclick" : true,
36739         /**
36740          * @event celldblclick
36741          * Fires when a cell is double clicked
36742          * @param {Grid} this
36743          * @param {Number} rowIndex
36744          * @param {Number} columnIndex
36745          * @param {Roo.EventObject} e
36746          */
36747         "celldblclick" : true,
36748         /**
36749          * @event rowclick
36750          * Fires when a row is clicked
36751          * @param {Grid} this
36752          * @param {Number} rowIndex
36753          * @param {Roo.EventObject} e
36754          */
36755         "rowclick" : true,
36756         /**
36757          * @event rowdblclick
36758          * Fires when a row is double clicked
36759          * @param {Grid} this
36760          * @param {Number} rowIndex
36761          * @param {Roo.EventObject} e
36762          */
36763         "rowdblclick" : true,
36764         /**
36765          * @event headerclick
36766          * Fires when a header is clicked
36767          * @param {Grid} this
36768          * @param {Number} columnIndex
36769          * @param {Roo.EventObject} e
36770          */
36771         "headerclick" : true,
36772         /**
36773          * @event headerdblclick
36774          * Fires when a header cell is double clicked
36775          * @param {Grid} this
36776          * @param {Number} columnIndex
36777          * @param {Roo.EventObject} e
36778          */
36779         "headerdblclick" : true,
36780         /**
36781          * @event rowcontextmenu
36782          * Fires when a row is right clicked
36783          * @param {Grid} this
36784          * @param {Number} rowIndex
36785          * @param {Roo.EventObject} e
36786          */
36787         "rowcontextmenu" : true,
36788         /**
36789          * @event cellcontextmenu
36790          * Fires when a cell is right clicked
36791          * @param {Grid} this
36792          * @param {Number} rowIndex
36793          * @param {Number} cellIndex
36794          * @param {Roo.EventObject} e
36795          */
36796          "cellcontextmenu" : true,
36797         /**
36798          * @event headercontextmenu
36799          * Fires when a header is right clicked
36800          * @param {Grid} this
36801          * @param {Number} columnIndex
36802          * @param {Roo.EventObject} e
36803          */
36804         "headercontextmenu" : true,
36805         /**
36806          * @event bodyscroll
36807          * Fires when the body element is scrolled
36808          * @param {Number} scrollLeft
36809          * @param {Number} scrollTop
36810          */
36811         "bodyscroll" : true,
36812         /**
36813          * @event columnresize
36814          * Fires when the user resizes a column
36815          * @param {Number} columnIndex
36816          * @param {Number} newSize
36817          */
36818         "columnresize" : true,
36819         /**
36820          * @event columnmove
36821          * Fires when the user moves a column
36822          * @param {Number} oldIndex
36823          * @param {Number} newIndex
36824          */
36825         "columnmove" : true,
36826         /**
36827          * @event startdrag
36828          * Fires when row(s) start being dragged
36829          * @param {Grid} this
36830          * @param {Roo.GridDD} dd The drag drop object
36831          * @param {event} e The raw browser event
36832          */
36833         "startdrag" : true,
36834         /**
36835          * @event enddrag
36836          * Fires when a drag operation is complete
36837          * @param {Grid} this
36838          * @param {Roo.GridDD} dd The drag drop object
36839          * @param {event} e The raw browser event
36840          */
36841         "enddrag" : true,
36842         /**
36843          * @event dragdrop
36844          * Fires when dragged row(s) are dropped on a valid DD target
36845          * @param {Grid} this
36846          * @param {Roo.GridDD} dd The drag drop object
36847          * @param {String} targetId The target drag drop object
36848          * @param {event} e The raw browser event
36849          */
36850         "dragdrop" : true,
36851         /**
36852          * @event dragover
36853          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
36854          * @param {Grid} this
36855          * @param {Roo.GridDD} dd The drag drop object
36856          * @param {String} targetId The target drag drop object
36857          * @param {event} e The raw browser event
36858          */
36859         "dragover" : true,
36860         /**
36861          * @event dragenter
36862          *  Fires when the dragged row(s) first cross another DD target while being dragged
36863          * @param {Grid} this
36864          * @param {Roo.GridDD} dd The drag drop object
36865          * @param {String} targetId The target drag drop object
36866          * @param {event} e The raw browser event
36867          */
36868         "dragenter" : true,
36869         /**
36870          * @event dragout
36871          * Fires when the dragged row(s) leave another DD target while being dragged
36872          * @param {Grid} this
36873          * @param {Roo.GridDD} dd The drag drop object
36874          * @param {String} targetId The target drag drop object
36875          * @param {event} e The raw browser event
36876          */
36877         "dragout" : true,
36878         /**
36879          * @event rowclass
36880          * Fires when a row is rendered, so you can change add a style to it.
36881          * @param {GridView} gridview   The grid view
36882          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
36883          */
36884         'rowclass' : true,
36885
36886         /**
36887          * @event render
36888          * Fires when the grid is rendered
36889          * @param {Grid} grid
36890          */
36891         'render' : true,
36892             /**
36893              * @event select
36894              * Fires when a date is selected
36895              * @param {DatePicker} this
36896              * @param {Date} date The selected date
36897              */
36898         'select': true,
36899         /**
36900              * @event monthchange
36901              * Fires when the displayed month changes 
36902              * @param {DatePicker} this
36903              * @param {Date} date The selected month
36904              */
36905         'monthchange': true,
36906         /**
36907              * @event evententer
36908              * Fires when mouse over an event
36909              * @param {Calendar} this
36910              * @param {event} Event
36911              */
36912         'evententer': true,
36913         /**
36914              * @event eventleave
36915              * Fires when the mouse leaves an
36916              * @param {Calendar} this
36917              * @param {event}
36918              */
36919         'eventleave': true,
36920         /**
36921              * @event eventclick
36922              * Fires when the mouse click an
36923              * @param {Calendar} this
36924              * @param {event}
36925              */
36926         'eventclick': true,
36927         /**
36928              * @event eventrender
36929              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
36930              * @param {Calendar} this
36931              * @param {data} data to be modified
36932              */
36933         'eventrender': true
36934         
36935     });
36936
36937     Roo.grid.Grid.superclass.constructor.call(this);
36938     this.on('render', function() {
36939         this.view.el.addClass('x-grid-cal'); 
36940         
36941         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
36942
36943     },this);
36944     
36945     if (!Roo.grid.Calendar.style) {
36946         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
36947             
36948             
36949             '.x-grid-cal .x-grid-col' :  {
36950                 height: 'auto !important',
36951                 'vertical-align': 'top'
36952             },
36953             '.x-grid-cal  .fc-event-hori' : {
36954                 height: '14px'
36955             }
36956              
36957             
36958         }, Roo.id());
36959     }
36960
36961     
36962     
36963 };
36964 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
36965     /**
36966      * @cfg {Store} eventStore The store that loads events.
36967      */
36968     eventStore : 25,
36969
36970      
36971     activeDate : false,
36972     startDay : 0,
36973     autoWidth : true,
36974     monitorWindowResize : false,
36975
36976     
36977     resizeColumns : function() {
36978         var col = (this.view.el.getWidth() / 7) - 3;
36979         // loop through cols, and setWidth
36980         for(var i =0 ; i < 7 ; i++){
36981             this.cm.setColumnWidth(i, col);
36982         }
36983     },
36984      setDate :function(date) {
36985         
36986         Roo.log('setDate?');
36987         
36988         this.resizeColumns();
36989         var vd = this.activeDate;
36990         this.activeDate = date;
36991 //        if(vd && this.el){
36992 //            var t = date.getTime();
36993 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
36994 //                Roo.log('using add remove');
36995 //                
36996 //                this.fireEvent('monthchange', this, date);
36997 //                
36998 //                this.cells.removeClass("fc-state-highlight");
36999 //                this.cells.each(function(c){
37000 //                   if(c.dateValue == t){
37001 //                       c.addClass("fc-state-highlight");
37002 //                       setTimeout(function(){
37003 //                            try{c.dom.firstChild.focus();}catch(e){}
37004 //                       }, 50);
37005 //                       return false;
37006 //                   }
37007 //                   return true;
37008 //                });
37009 //                return;
37010 //            }
37011 //        }
37012         
37013         var days = date.getDaysInMonth();
37014         
37015         var firstOfMonth = date.getFirstDateOfMonth();
37016         var startingPos = firstOfMonth.getDay()-this.startDay;
37017         
37018         if(startingPos < this.startDay){
37019             startingPos += 7;
37020         }
37021         
37022         var pm = date.add(Date.MONTH, -1);
37023         var prevStart = pm.getDaysInMonth()-startingPos;
37024 //        
37025         
37026         
37027         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37028         
37029         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37030         //this.cells.addClassOnOver('fc-state-hover');
37031         
37032         var cells = this.cells.elements;
37033         var textEls = this.textNodes;
37034         
37035         //Roo.each(cells, function(cell){
37036         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37037         //});
37038         
37039         days += startingPos;
37040
37041         // convert everything to numbers so it's fast
37042         var day = 86400000;
37043         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37044         //Roo.log(d);
37045         //Roo.log(pm);
37046         //Roo.log(prevStart);
37047         
37048         var today = new Date().clearTime().getTime();
37049         var sel = date.clearTime().getTime();
37050         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37051         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37052         var ddMatch = this.disabledDatesRE;
37053         var ddText = this.disabledDatesText;
37054         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37055         var ddaysText = this.disabledDaysText;
37056         var format = this.format;
37057         
37058         var setCellClass = function(cal, cell){
37059             
37060             //Roo.log('set Cell Class');
37061             cell.title = "";
37062             var t = d.getTime();
37063             
37064             //Roo.log(d);
37065             
37066             
37067             cell.dateValue = t;
37068             if(t == today){
37069                 cell.className += " fc-today";
37070                 cell.className += " fc-state-highlight";
37071                 cell.title = cal.todayText;
37072             }
37073             if(t == sel){
37074                 // disable highlight in other month..
37075                 cell.className += " fc-state-highlight";
37076                 
37077             }
37078             // disabling
37079             if(t < min) {
37080                 //cell.className = " fc-state-disabled";
37081                 cell.title = cal.minText;
37082                 return;
37083             }
37084             if(t > max) {
37085                 //cell.className = " fc-state-disabled";
37086                 cell.title = cal.maxText;
37087                 return;
37088             }
37089             if(ddays){
37090                 if(ddays.indexOf(d.getDay()) != -1){
37091                     // cell.title = ddaysText;
37092                    // cell.className = " fc-state-disabled";
37093                 }
37094             }
37095             if(ddMatch && format){
37096                 var fvalue = d.dateFormat(format);
37097                 if(ddMatch.test(fvalue)){
37098                     cell.title = ddText.replace("%0", fvalue);
37099                    cell.className = " fc-state-disabled";
37100                 }
37101             }
37102             
37103             if (!cell.initialClassName) {
37104                 cell.initialClassName = cell.dom.className;
37105             }
37106             
37107             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
37108         };
37109
37110         var i = 0;
37111         
37112         for(; i < startingPos; i++) {
37113             cells[i].dayName =  (++prevStart);
37114             Roo.log(textEls[i]);
37115             d.setDate(d.getDate()+1);
37116             
37117             //cells[i].className = "fc-past fc-other-month";
37118             setCellClass(this, cells[i]);
37119         }
37120         
37121         var intDay = 0;
37122         
37123         for(; i < days; i++){
37124             intDay = i - startingPos + 1;
37125             cells[i].dayName =  (intDay);
37126             d.setDate(d.getDate()+1);
37127             
37128             cells[i].className = ''; // "x-date-active";
37129             setCellClass(this, cells[i]);
37130         }
37131         var extraDays = 0;
37132         
37133         for(; i < 42; i++) {
37134             //textEls[i].innerHTML = (++extraDays);
37135             
37136             d.setDate(d.getDate()+1);
37137             cells[i].dayName = (++extraDays);
37138             cells[i].className = "fc-future fc-other-month";
37139             setCellClass(this, cells[i]);
37140         }
37141         
37142         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37143         
37144         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37145         
37146         // this will cause all the cells to mis
37147         var rows= [];
37148         var i =0;
37149         for (var r = 0;r < 6;r++) {
37150             for (var c =0;c < 7;c++) {
37151                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37152             }    
37153         }
37154         
37155         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37156         for(i=0;i<cells.length;i++) {
37157             
37158             this.cells.elements[i].dayName = cells[i].dayName ;
37159             this.cells.elements[i].className = cells[i].className;
37160             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37161             this.cells.elements[i].title = cells[i].title ;
37162             this.cells.elements[i].dateValue = cells[i].dateValue ;
37163         }
37164         
37165         
37166         
37167         
37168         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37169         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37170         
37171         ////if(totalRows != 6){
37172             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37173            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37174        // }
37175         
37176         this.fireEvent('monthchange', this, date);
37177         
37178         
37179     },
37180  /**
37181      * Returns the grid's SelectionModel.
37182      * @return {SelectionModel}
37183      */
37184     getSelectionModel : function(){
37185         if(!this.selModel){
37186             this.selModel = new Roo.grid.CellSelectionModel();
37187         }
37188         return this.selModel;
37189     },
37190
37191     load: function() {
37192         this.eventStore.load()
37193         
37194         
37195         
37196     },
37197     
37198     findCell : function(dt) {
37199         dt = dt.clearTime().getTime();
37200         var ret = false;
37201         this.cells.each(function(c){
37202             //Roo.log("check " +c.dateValue + '?=' + dt);
37203             if(c.dateValue == dt){
37204                 ret = c;
37205                 return false;
37206             }
37207             return true;
37208         });
37209         
37210         return ret;
37211     },
37212     
37213     findCells : function(rec) {
37214         var s = rec.data.start_dt.clone().clearTime().getTime();
37215        // Roo.log(s);
37216         var e= rec.data.end_dt.clone().clearTime().getTime();
37217        // Roo.log(e);
37218         var ret = [];
37219         this.cells.each(function(c){
37220              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37221             
37222             if(c.dateValue > e){
37223                 return ;
37224             }
37225             if(c.dateValue < s){
37226                 return ;
37227             }
37228             ret.push(c);
37229         });
37230         
37231         return ret;    
37232     },
37233     
37234     findBestRow: function(cells)
37235     {
37236         var ret = 0;
37237         
37238         for (var i =0 ; i < cells.length;i++) {
37239             ret  = Math.max(cells[i].rows || 0,ret);
37240         }
37241         return ret;
37242         
37243     },
37244     
37245     
37246     addItem : function(rec)
37247     {
37248         // look for vertical location slot in
37249         var cells = this.findCells(rec);
37250         
37251         rec.row = this.findBestRow(cells);
37252         
37253         // work out the location.
37254         
37255         var crow = false;
37256         var rows = [];
37257         for(var i =0; i < cells.length; i++) {
37258             if (!crow) {
37259                 crow = {
37260                     start : cells[i],
37261                     end :  cells[i]
37262                 };
37263                 continue;
37264             }
37265             if (crow.start.getY() == cells[i].getY()) {
37266                 // on same row.
37267                 crow.end = cells[i];
37268                 continue;
37269             }
37270             // different row.
37271             rows.push(crow);
37272             crow = {
37273                 start: cells[i],
37274                 end : cells[i]
37275             };
37276             
37277         }
37278         
37279         rows.push(crow);
37280         rec.els = [];
37281         rec.rows = rows;
37282         rec.cells = cells;
37283         for (var i = 0; i < cells.length;i++) {
37284             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
37285             
37286         }
37287         
37288         
37289     },
37290     
37291     clearEvents: function() {
37292         
37293         if (!this.eventStore.getCount()) {
37294             return;
37295         }
37296         // reset number of rows in cells.
37297         Roo.each(this.cells.elements, function(c){
37298             c.rows = 0;
37299         });
37300         
37301         this.eventStore.each(function(e) {
37302             this.clearEvent(e);
37303         },this);
37304         
37305     },
37306     
37307     clearEvent : function(ev)
37308     {
37309         if (ev.els) {
37310             Roo.each(ev.els, function(el) {
37311                 el.un('mouseenter' ,this.onEventEnter, this);
37312                 el.un('mouseleave' ,this.onEventLeave, this);
37313                 el.remove();
37314             },this);
37315             ev.els = [];
37316         }
37317     },
37318     
37319     
37320     renderEvent : function(ev,ctr) {
37321         if (!ctr) {
37322              ctr = this.view.el.select('.fc-event-container',true).first();
37323         }
37324         
37325          
37326         this.clearEvent(ev);
37327             //code
37328        
37329         
37330         
37331         ev.els = [];
37332         var cells = ev.cells;
37333         var rows = ev.rows;
37334         this.fireEvent('eventrender', this, ev);
37335         
37336         for(var i =0; i < rows.length; i++) {
37337             
37338             cls = '';
37339             if (i == 0) {
37340                 cls += ' fc-event-start';
37341             }
37342             if ((i+1) == rows.length) {
37343                 cls += ' fc-event-end';
37344             }
37345             
37346             //Roo.log(ev.data);
37347             // how many rows should it span..
37348             var cg = this.eventTmpl.append(ctr,Roo.apply({
37349                 fccls : cls
37350                 
37351             }, ev.data) , true);
37352             
37353             
37354             cg.on('mouseenter' ,this.onEventEnter, this, ev);
37355             cg.on('mouseleave' ,this.onEventLeave, this, ev);
37356             cg.on('click', this.onEventClick, this, ev);
37357             
37358             ev.els.push(cg);
37359             
37360             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
37361             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
37362             //Roo.log(cg);
37363              
37364             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
37365             cg.setWidth(ebox.right - sbox.x -2);
37366         }
37367     },
37368     
37369     renderEvents: function()
37370     {   
37371         // first make sure there is enough space..
37372         
37373         if (!this.eventTmpl) {
37374             this.eventTmpl = new Roo.Template(
37375                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
37376                     '<div class="fc-event-inner">' +
37377                         '<span class="fc-event-time">{time}</span>' +
37378                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
37379                     '</div>' +
37380                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
37381                 '</div>'
37382             );
37383                 
37384         }
37385                
37386         
37387         
37388         this.cells.each(function(c) {
37389             //Roo.log(c.select('.fc-day-content div',true).first());
37390             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
37391         });
37392         
37393         var ctr = this.view.el.select('.fc-event-container',true).first();
37394         
37395         var cls;
37396         this.eventStore.each(function(ev){
37397             
37398             this.renderEvent(ev);
37399              
37400              
37401         }, this);
37402         this.view.layout();
37403         
37404     },
37405     
37406     onEventEnter: function (e, el,event,d) {
37407         this.fireEvent('evententer', this, el, event);
37408     },
37409     
37410     onEventLeave: function (e, el,event,d) {
37411         this.fireEvent('eventleave', this, el, event);
37412     },
37413     
37414     onEventClick: function (e, el,event,d) {
37415         this.fireEvent('eventclick', this, el, event);
37416     },
37417     
37418     onMonthChange: function () {
37419         this.store.load();
37420     },
37421     
37422     onLoad: function () {
37423         
37424         //Roo.log('calendar onload');
37425 //         
37426         if(this.eventStore.getCount() > 0){
37427             
37428            
37429             
37430             this.eventStore.each(function(d){
37431                 
37432                 
37433                 // FIXME..
37434                 var add =   d.data;
37435                 if (typeof(add.end_dt) == 'undefined')  {
37436                     Roo.log("Missing End time in calendar data: ");
37437                     Roo.log(d);
37438                     return;
37439                 }
37440                 if (typeof(add.start_dt) == 'undefined')  {
37441                     Roo.log("Missing Start time in calendar data: ");
37442                     Roo.log(d);
37443                     return;
37444                 }
37445                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
37446                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
37447                 add.id = add.id || d.id;
37448                 add.title = add.title || '??';
37449                 
37450                 this.addItem(d);
37451                 
37452              
37453             },this);
37454         }
37455         
37456         this.renderEvents();
37457     }
37458     
37459
37460 });
37461 /*
37462  grid : {
37463                 xtype: 'Grid',
37464                 xns: Roo.grid,
37465                 listeners : {
37466                     render : function ()
37467                     {
37468                         _this.grid = this;
37469                         
37470                         if (!this.view.el.hasClass('course-timesheet')) {
37471                             this.view.el.addClass('course-timesheet');
37472                         }
37473                         if (this.tsStyle) {
37474                             this.ds.load({});
37475                             return; 
37476                         }
37477                         Roo.log('width');
37478                         Roo.log(_this.grid.view.el.getWidth());
37479                         
37480                         
37481                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
37482                             '.course-timesheet .x-grid-row' : {
37483                                 height: '80px'
37484                             },
37485                             '.x-grid-row td' : {
37486                                 'vertical-align' : 0
37487                             },
37488                             '.course-edit-link' : {
37489                                 'color' : 'blue',
37490                                 'text-overflow' : 'ellipsis',
37491                                 'overflow' : 'hidden',
37492                                 'white-space' : 'nowrap',
37493                                 'cursor' : 'pointer'
37494                             },
37495                             '.sub-link' : {
37496                                 'color' : 'green'
37497                             },
37498                             '.de-act-sup-link' : {
37499                                 'color' : 'purple',
37500                                 'text-decoration' : 'line-through'
37501                             },
37502                             '.de-act-link' : {
37503                                 'color' : 'red',
37504                                 'text-decoration' : 'line-through'
37505                             },
37506                             '.course-timesheet .course-highlight' : {
37507                                 'border-top-style': 'dashed !important',
37508                                 'border-bottom-bottom': 'dashed !important'
37509                             },
37510                             '.course-timesheet .course-item' : {
37511                                 'font-family'   : 'tahoma, arial, helvetica',
37512                                 'font-size'     : '11px',
37513                                 'overflow'      : 'hidden',
37514                                 'padding-left'  : '10px',
37515                                 'padding-right' : '10px',
37516                                 'padding-top' : '10px' 
37517                             }
37518                             
37519                         }, Roo.id());
37520                                 this.ds.load({});
37521                     }
37522                 },
37523                 autoWidth : true,
37524                 monitorWindowResize : false,
37525                 cellrenderer : function(v,x,r)
37526                 {
37527                     return v;
37528                 },
37529                 sm : {
37530                     xtype: 'CellSelectionModel',
37531                     xns: Roo.grid
37532                 },
37533                 dataSource : {
37534                     xtype: 'Store',
37535                     xns: Roo.data,
37536                     listeners : {
37537                         beforeload : function (_self, options)
37538                         {
37539                             options.params = options.params || {};
37540                             options.params._month = _this.monthField.getValue();
37541                             options.params.limit = 9999;
37542                             options.params['sort'] = 'when_dt';    
37543                             options.params['dir'] = 'ASC';    
37544                             this.proxy.loadResponse = this.loadResponse;
37545                             Roo.log("load?");
37546                             //this.addColumns();
37547                         },
37548                         load : function (_self, records, options)
37549                         {
37550                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
37551                                 // if you click on the translation.. you can edit it...
37552                                 var el = Roo.get(this);
37553                                 var id = el.dom.getAttribute('data-id');
37554                                 var d = el.dom.getAttribute('data-date');
37555                                 var t = el.dom.getAttribute('data-time');
37556                                 //var id = this.child('span').dom.textContent;
37557                                 
37558                                 //Roo.log(this);
37559                                 Pman.Dialog.CourseCalendar.show({
37560                                     id : id,
37561                                     when_d : d,
37562                                     when_t : t,
37563                                     productitem_active : id ? 1 : 0
37564                                 }, function() {
37565                                     _this.grid.ds.load({});
37566                                 });
37567                            
37568                            });
37569                            
37570                            _this.panel.fireEvent('resize', [ '', '' ]);
37571                         }
37572                     },
37573                     loadResponse : function(o, success, response){
37574                             // this is overridden on before load..
37575                             
37576                             Roo.log("our code?");       
37577                             //Roo.log(success);
37578                             //Roo.log(response)
37579                             delete this.activeRequest;
37580                             if(!success){
37581                                 this.fireEvent("loadexception", this, o, response);
37582                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
37583                                 return;
37584                             }
37585                             var result;
37586                             try {
37587                                 result = o.reader.read(response);
37588                             }catch(e){
37589                                 Roo.log("load exception?");
37590                                 this.fireEvent("loadexception", this, o, response, e);
37591                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
37592                                 return;
37593                             }
37594                             Roo.log("ready...");        
37595                             // loop through result.records;
37596                             // and set this.tdate[date] = [] << array of records..
37597                             _this.tdata  = {};
37598                             Roo.each(result.records, function(r){
37599                                 //Roo.log(r.data);
37600                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
37601                                     _this.tdata[r.data.when_dt.format('j')] = [];
37602                                 }
37603                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
37604                             });
37605                             
37606                             //Roo.log(_this.tdata);
37607                             
37608                             result.records = [];
37609                             result.totalRecords = 6;
37610                     
37611                             // let's generate some duumy records for the rows.
37612                             //var st = _this.dateField.getValue();
37613                             
37614                             // work out monday..
37615                             //st = st.add(Date.DAY, -1 * st.format('w'));
37616                             
37617                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37618                             
37619                             var firstOfMonth = date.getFirstDayOfMonth();
37620                             var days = date.getDaysInMonth();
37621                             var d = 1;
37622                             var firstAdded = false;
37623                             for (var i = 0; i < result.totalRecords ; i++) {
37624                                 //var d= st.add(Date.DAY, i);
37625                                 var row = {};
37626                                 var added = 0;
37627                                 for(var w = 0 ; w < 7 ; w++){
37628                                     if(!firstAdded && firstOfMonth != w){
37629                                         continue;
37630                                     }
37631                                     if(d > days){
37632                                         continue;
37633                                     }
37634                                     firstAdded = true;
37635                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
37636                                     row['weekday'+w] = String.format(
37637                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
37638                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
37639                                                     d,
37640                                                     date.format('Y-m-')+dd
37641                                                 );
37642                                     added++;
37643                                     if(typeof(_this.tdata[d]) != 'undefined'){
37644                                         Roo.each(_this.tdata[d], function(r){
37645                                             var is_sub = '';
37646                                             var deactive = '';
37647                                             var id = r.id;
37648                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
37649                                             if(r.parent_id*1>0){
37650                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
37651                                                 id = r.parent_id;
37652                                             }
37653                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
37654                                                 deactive = 'de-act-link';
37655                                             }
37656                                             
37657                                             row['weekday'+w] += String.format(
37658                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
37659                                                     id, //0
37660                                                     r.product_id_name, //1
37661                                                     r.when_dt.format('h:ia'), //2
37662                                                     is_sub, //3
37663                                                     deactive, //4
37664                                                     desc // 5
37665                                             );
37666                                         });
37667                                     }
37668                                     d++;
37669                                 }
37670                                 
37671                                 // only do this if something added..
37672                                 if(added > 0){ 
37673                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
37674                                 }
37675                                 
37676                                 
37677                                 // push it twice. (second one with an hour..
37678                                 
37679                             }
37680                             //Roo.log(result);
37681                             this.fireEvent("load", this, o, o.request.arg);
37682                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
37683                         },
37684                     sortInfo : {field: 'when_dt', direction : 'ASC' },
37685                     proxy : {
37686                         xtype: 'HttpProxy',
37687                         xns: Roo.data,
37688                         method : 'GET',
37689                         url : baseURL + '/Roo/Shop_course.php'
37690                     },
37691                     reader : {
37692                         xtype: 'JsonReader',
37693                         xns: Roo.data,
37694                         id : 'id',
37695                         fields : [
37696                             {
37697                                 'name': 'id',
37698                                 'type': 'int'
37699                             },
37700                             {
37701                                 'name': 'when_dt',
37702                                 'type': 'string'
37703                             },
37704                             {
37705                                 'name': 'end_dt',
37706                                 'type': 'string'
37707                             },
37708                             {
37709                                 'name': 'parent_id',
37710                                 'type': 'int'
37711                             },
37712                             {
37713                                 'name': 'product_id',
37714                                 'type': 'int'
37715                             },
37716                             {
37717                                 'name': 'productitem_id',
37718                                 'type': 'int'
37719                             },
37720                             {
37721                                 'name': 'guid',
37722                                 'type': 'int'
37723                             }
37724                         ]
37725                     }
37726                 },
37727                 toolbar : {
37728                     xtype: 'Toolbar',
37729                     xns: Roo,
37730                     items : [
37731                         {
37732                             xtype: 'Button',
37733                             xns: Roo.Toolbar,
37734                             listeners : {
37735                                 click : function (_self, e)
37736                                 {
37737                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37738                                     sd.setMonth(sd.getMonth()-1);
37739                                     _this.monthField.setValue(sd.format('Y-m-d'));
37740                                     _this.grid.ds.load({});
37741                                 }
37742                             },
37743                             text : "Back"
37744                         },
37745                         {
37746                             xtype: 'Separator',
37747                             xns: Roo.Toolbar
37748                         },
37749                         {
37750                             xtype: 'MonthField',
37751                             xns: Roo.form,
37752                             listeners : {
37753                                 render : function (_self)
37754                                 {
37755                                     _this.monthField = _self;
37756                                    // _this.monthField.set  today
37757                                 },
37758                                 select : function (combo, date)
37759                                 {
37760                                     _this.grid.ds.load({});
37761                                 }
37762                             },
37763                             value : (function() { return new Date(); })()
37764                         },
37765                         {
37766                             xtype: 'Separator',
37767                             xns: Roo.Toolbar
37768                         },
37769                         {
37770                             xtype: 'TextItem',
37771                             xns: Roo.Toolbar,
37772                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
37773                         },
37774                         {
37775                             xtype: 'Fill',
37776                             xns: Roo.Toolbar
37777                         },
37778                         {
37779                             xtype: 'Button',
37780                             xns: Roo.Toolbar,
37781                             listeners : {
37782                                 click : function (_self, e)
37783                                 {
37784                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37785                                     sd.setMonth(sd.getMonth()+1);
37786                                     _this.monthField.setValue(sd.format('Y-m-d'));
37787                                     _this.grid.ds.load({});
37788                                 }
37789                             },
37790                             text : "Next"
37791                         }
37792                     ]
37793                 },
37794                  
37795             }
37796         };
37797         
37798         *//*
37799  * Based on:
37800  * Ext JS Library 1.1.1
37801  * Copyright(c) 2006-2007, Ext JS, LLC.
37802  *
37803  * Originally Released Under LGPL - original licence link has changed is not relivant.
37804  *
37805  * Fork - LGPL
37806  * <script type="text/javascript">
37807  */
37808  
37809 /**
37810  * @class Roo.LoadMask
37811  * A simple utility class for generically masking elements while loading data.  If the element being masked has
37812  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
37813  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
37814  * element's UpdateManager load indicator and will be destroyed after the initial load.
37815  * @constructor
37816  * Create a new LoadMask
37817  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
37818  * @param {Object} config The config object
37819  */
37820 Roo.LoadMask = function(el, config){
37821     this.el = Roo.get(el);
37822     Roo.apply(this, config);
37823     if(this.store){
37824         this.store.on('beforeload', this.onBeforeLoad, this);
37825         this.store.on('load', this.onLoad, this);
37826         this.store.on('loadexception', this.onLoadException, this);
37827         this.removeMask = false;
37828     }else{
37829         var um = this.el.getUpdateManager();
37830         um.showLoadIndicator = false; // disable the default indicator
37831         um.on('beforeupdate', this.onBeforeLoad, this);
37832         um.on('update', this.onLoad, this);
37833         um.on('failure', this.onLoad, this);
37834         this.removeMask = true;
37835     }
37836 };
37837
37838 Roo.LoadMask.prototype = {
37839     /**
37840      * @cfg {Boolean} removeMask
37841      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
37842      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
37843      */
37844     /**
37845      * @cfg {String} msg
37846      * The text to display in a centered loading message box (defaults to 'Loading...')
37847      */
37848     msg : 'Loading...',
37849     /**
37850      * @cfg {String} msgCls
37851      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
37852      */
37853     msgCls : 'x-mask-loading',
37854
37855     /**
37856      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
37857      * @type Boolean
37858      */
37859     disabled: false,
37860
37861     /**
37862      * Disables the mask to prevent it from being displayed
37863      */
37864     disable : function(){
37865        this.disabled = true;
37866     },
37867
37868     /**
37869      * Enables the mask so that it can be displayed
37870      */
37871     enable : function(){
37872         this.disabled = false;
37873     },
37874     
37875     onLoadException : function()
37876     {
37877         Roo.log(arguments);
37878         
37879         if (typeof(arguments[3]) != 'undefined') {
37880             Roo.MessageBox.alert("Error loading",arguments[3]);
37881         } 
37882         /*
37883         try {
37884             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
37885                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
37886             }   
37887         } catch(e) {
37888             
37889         }
37890         */
37891     
37892         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
37893     },
37894     // private
37895     onLoad : function()
37896     {
37897         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
37898     },
37899
37900     // private
37901     onBeforeLoad : function(){
37902         if(!this.disabled){
37903             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
37904         }
37905     },
37906
37907     // private
37908     destroy : function(){
37909         if(this.store){
37910             this.store.un('beforeload', this.onBeforeLoad, this);
37911             this.store.un('load', this.onLoad, this);
37912             this.store.un('loadexception', this.onLoadException, this);
37913         }else{
37914             var um = this.el.getUpdateManager();
37915             um.un('beforeupdate', this.onBeforeLoad, this);
37916             um.un('update', this.onLoad, this);
37917             um.un('failure', this.onLoad, this);
37918         }
37919     }
37920 };/*
37921  * Based on:
37922  * Ext JS Library 1.1.1
37923  * Copyright(c) 2006-2007, Ext JS, LLC.
37924  *
37925  * Originally Released Under LGPL - original licence link has changed is not relivant.
37926  *
37927  * Fork - LGPL
37928  * <script type="text/javascript">
37929  */
37930
37931
37932 /**
37933  * @class Roo.XTemplate
37934  * @extends Roo.Template
37935  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
37936 <pre><code>
37937 var t = new Roo.XTemplate(
37938         '&lt;select name="{name}"&gt;',
37939                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
37940         '&lt;/select&gt;'
37941 );
37942  
37943 // then append, applying the master template values
37944  </code></pre>
37945  *
37946  * Supported features:
37947  *
37948  *  Tags:
37949
37950 <pre><code>
37951       {a_variable} - output encoded.
37952       {a_variable.format:("Y-m-d")} - call a method on the variable
37953       {a_variable:raw} - unencoded output
37954       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
37955       {a_variable:this.method_on_template(...)} - call a method on the template object.
37956  
37957 </code></pre>
37958  *  The tpl tag:
37959 <pre><code>
37960         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
37961         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
37962         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
37963         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
37964   
37965         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
37966         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
37967 </code></pre>
37968  *      
37969  */
37970 Roo.XTemplate = function()
37971 {
37972     Roo.XTemplate.superclass.constructor.apply(this, arguments);
37973     if (this.html) {
37974         this.compile();
37975     }
37976 };
37977
37978
37979 Roo.extend(Roo.XTemplate, Roo.Template, {
37980
37981     /**
37982      * The various sub templates
37983      */
37984     tpls : false,
37985     /**
37986      *
37987      * basic tag replacing syntax
37988      * WORD:WORD()
37989      *
37990      * // you can fake an object call by doing this
37991      *  x.t:(test,tesT) 
37992      * 
37993      */
37994     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
37995
37996     /**
37997      * compile the template
37998      *
37999      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38000      *
38001      */
38002     compile: function()
38003     {
38004         var s = this.html;
38005      
38006         s = ['<tpl>', s, '</tpl>'].join('');
38007     
38008         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38009             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38010             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38011             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38012             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38013             m,
38014             id     = 0,
38015             tpls   = [];
38016     
38017         while(true == !!(m = s.match(re))){
38018             var forMatch   = m[0].match(nameRe),
38019                 ifMatch   = m[0].match(ifRe),
38020                 execMatch   = m[0].match(execRe),
38021                 namedMatch   = m[0].match(namedRe),
38022                 
38023                 exp  = null, 
38024                 fn   = null,
38025                 exec = null,
38026                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38027                 
38028             if (ifMatch) {
38029                 // if - puts fn into test..
38030                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38031                 if(exp){
38032                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38033                 }
38034             }
38035             
38036             if (execMatch) {
38037                 // exec - calls a function... returns empty if true is  returned.
38038                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38039                 if(exp){
38040                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38041                 }
38042             }
38043             
38044             
38045             if (name) {
38046                 // for = 
38047                 switch(name){
38048                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38049                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38050                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38051                 }
38052             }
38053             var uid = namedMatch ? namedMatch[1] : id;
38054             
38055             
38056             tpls.push({
38057                 id:     namedMatch ? namedMatch[1] : id,
38058                 target: name,
38059                 exec:   exec,
38060                 test:   fn,
38061                 body:   m[1] || ''
38062             });
38063             if (namedMatch) {
38064                 s = s.replace(m[0], '');
38065             } else { 
38066                 s = s.replace(m[0], '{xtpl'+ id + '}');
38067             }
38068             ++id;
38069         }
38070         this.tpls = [];
38071         for(var i = tpls.length-1; i >= 0; --i){
38072             this.compileTpl(tpls[i]);
38073             this.tpls[tpls[i].id] = tpls[i];
38074         }
38075         this.master = tpls[tpls.length-1];
38076         return this;
38077     },
38078     /**
38079      * same as applyTemplate, except it's done to one of the subTemplates
38080      * when using named templates, you can do:
38081      *
38082      * var str = pl.applySubTemplate('your-name', values);
38083      *
38084      * 
38085      * @param {Number} id of the template
38086      * @param {Object} values to apply to template
38087      * @param {Object} parent (normaly the instance of this object)
38088      */
38089     applySubTemplate : function(id, values, parent)
38090     {
38091         
38092         
38093         var t = this.tpls[id];
38094         
38095         
38096         try { 
38097             if(t.test && !t.test.call(this, values, parent)){
38098                 return '';
38099             }
38100         } catch(e) {
38101             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38102             Roo.log(e.toString());
38103             Roo.log(t.test);
38104             return ''
38105         }
38106         try { 
38107             
38108             if(t.exec && t.exec.call(this, values, parent)){
38109                 return '';
38110             }
38111         } catch(e) {
38112             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38113             Roo.log(e.toString());
38114             Roo.log(t.exec);
38115             return ''
38116         }
38117         try {
38118             var vs = t.target ? t.target.call(this, values, parent) : values;
38119             parent = t.target ? values : parent;
38120             if(t.target && vs instanceof Array){
38121                 var buf = [];
38122                 for(var i = 0, len = vs.length; i < len; i++){
38123                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38124                 }
38125                 return buf.join('');
38126             }
38127             return t.compiled.call(this, vs, parent);
38128         } catch (e) {
38129             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38130             Roo.log(e.toString());
38131             Roo.log(t.compiled);
38132             return '';
38133         }
38134     },
38135
38136     compileTpl : function(tpl)
38137     {
38138         var fm = Roo.util.Format;
38139         var useF = this.disableFormats !== true;
38140         var sep = Roo.isGecko ? "+" : ",";
38141         var undef = function(str) {
38142             Roo.log("Property not found :"  + str);
38143             return '';
38144         };
38145         
38146         var fn = function(m, name, format, args)
38147         {
38148             //Roo.log(arguments);
38149             args = args ? args.replace(/\\'/g,"'") : args;
38150             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38151             if (typeof(format) == 'undefined') {
38152                 format= 'htmlEncode';
38153             }
38154             if (format == 'raw' ) {
38155                 format = false;
38156             }
38157             
38158             if(name.substr(0, 4) == 'xtpl'){
38159                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38160             }
38161             
38162             // build an array of options to determine if value is undefined..
38163             
38164             // basically get 'xxxx.yyyy' then do
38165             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38166             //    (function () { Roo.log("Property not found"); return ''; })() :
38167             //    ......
38168             
38169             var udef_ar = [];
38170             var lookfor = '';
38171             Roo.each(name.split('.'), function(st) {
38172                 lookfor += (lookfor.length ? '.': '') + st;
38173                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38174             });
38175             
38176             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38177             
38178             
38179             if(format && useF){
38180                 
38181                 args = args ? ',' + args : "";
38182                  
38183                 if(format.substr(0, 5) != "this."){
38184                     format = "fm." + format + '(';
38185                 }else{
38186                     format = 'this.call("'+ format.substr(5) + '", ';
38187                     args = ", values";
38188                 }
38189                 
38190                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38191             }
38192              
38193             if (args.length) {
38194                 // called with xxyx.yuu:(test,test)
38195                 // change to ()
38196                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38197             }
38198             // raw.. - :raw modifier..
38199             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38200             
38201         };
38202         var body;
38203         // branched to use + in gecko and [].join() in others
38204         if(Roo.isGecko){
38205             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38206                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38207                     "';};};";
38208         }else{
38209             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38210             body.push(tpl.body.replace(/(\r\n|\n)/g,
38211                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38212             body.push("'].join('');};};");
38213             body = body.join('');
38214         }
38215         
38216         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38217        
38218         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38219         eval(body);
38220         
38221         return this;
38222     },
38223
38224     applyTemplate : function(values){
38225         return this.master.compiled.call(this, values, {});
38226         //var s = this.subs;
38227     },
38228
38229     apply : function(){
38230         return this.applyTemplate.apply(this, arguments);
38231     }
38232
38233  });
38234
38235 Roo.XTemplate.from = function(el){
38236     el = Roo.getDom(el);
38237     return new Roo.XTemplate(el.value || el.innerHTML);
38238 };